mirror of
https://github.com/espressif/esptool.git
synced 2025-10-18 18:01:15 +08:00
fix(elf2image): Try to correct MMU page size if not specified
This commit fixes issue with using elf2image command without --flash-mmu-page-size. In that case, app info segment might be incorrectly placed in the image. This is fixed by checking if app info segment is present and if so, use page size from it or from its alignment. Closes https://github.com/espressif/esptool/issues/1062
This commit is contained in:
@@ -11,7 +11,7 @@ import os
|
||||
import re
|
||||
import struct
|
||||
import tempfile
|
||||
from typing import IO, Optional
|
||||
from typing import IO, Optional, Tuple
|
||||
|
||||
from intelhex import HexRecordError, IntelHex
|
||||
|
||||
@@ -180,6 +180,7 @@ class ELFSection(ImageSegment):
|
||||
class BaseFirmwareImage(object):
|
||||
SEG_HEADER_LEN = 8
|
||||
SHA256_DIGEST_LEN = 32
|
||||
MMU_PAGE_SIZE_CONF: Tuple[int, ...] = ()
|
||||
|
||||
""" Base class with common firmware image functions """
|
||||
|
||||
@@ -621,6 +622,8 @@ class ESP32FirmwareImage(BaseFirmwareImage):
|
||||
|
||||
IROM_ALIGN = 65536
|
||||
|
||||
MMU_PAGE_SIZE_CONF: Tuple[int, ...] = (IROM_ALIGN,)
|
||||
|
||||
def __init__(self, load_file=None, append_digest=True, ram_only_header=False):
|
||||
super(ESP32FirmwareImage, self).__init__()
|
||||
self.secure_pad = None
|
||||
@@ -1128,12 +1131,13 @@ class ESP32C2FirmwareImage(ESP32FirmwareImage):
|
||||
"""ESP32C2 Firmware Image almost exactly the same as ESP32FirmwareImage"""
|
||||
|
||||
ROM_LOADER = ESP32C2ROM
|
||||
MMU_PAGE_SIZE_CONF = (16384, 32768, 65536)
|
||||
|
||||
def set_mmu_page_size(self, size):
|
||||
if size not in [16384, 32768, 65536]:
|
||||
if size not in self.MMU_PAGE_SIZE_CONF:
|
||||
valid_sizes = ", ".join(f"{x // 1024}KB" for x in self.MMU_PAGE_SIZE_CONF)
|
||||
raise FatalError(
|
||||
"{} bytes is not a valid ESP32-C2 page size, "
|
||||
"select from 64KB, 32KB, 16KB.".format(size)
|
||||
f"{size} bytes is not a valid {self.ROM_LOADER.CHIP_NAME} page size, select from {valid_sizes}."
|
||||
)
|
||||
self.IROM_ALIGN = size
|
||||
|
||||
@@ -1145,12 +1149,13 @@ class ESP32C6FirmwareImage(ESP32FirmwareImage):
|
||||
"""ESP32C6 Firmware Image almost exactly the same as ESP32FirmwareImage"""
|
||||
|
||||
ROM_LOADER = ESP32C6ROM
|
||||
MMU_PAGE_SIZE_CONF = (8192, 16384, 32768, 65536)
|
||||
|
||||
def set_mmu_page_size(self, size):
|
||||
if size not in [8192, 16384, 32768, 65536]:
|
||||
if size not in self.MMU_PAGE_SIZE_CONF:
|
||||
valid_sizes = ", ".join(f"{x // 1024}KB" for x in self.MMU_PAGE_SIZE_CONF)
|
||||
raise FatalError(
|
||||
"{} bytes is not a valid ESP32-C6 page size, "
|
||||
"select from 64KB, 32KB, 16KB, 8KB.".format(size)
|
||||
f"{size} bytes is not a valid {self.ROM_LOADER.CHIP_NAME} page size, select from {valid_sizes}."
|
||||
)
|
||||
self.IROM_ALIGN = size
|
||||
|
||||
@@ -1167,8 +1172,8 @@ class ESP32C61FirmwareImage(ESP32C6FirmwareImage):
|
||||
ESP32C61ROM.BOOTLOADER_IMAGE = ESP32C61FirmwareImage
|
||||
|
||||
|
||||
class ESP32C5FirmwareImage(ESP32C6FirmwareImage):
|
||||
"""ESP32C5 Firmware Image almost exactly the same as ESP32C6FirmwareImage"""
|
||||
class ESP32C5FirmwareImage(ESP32FirmwareImage):
|
||||
"""ESP32C5 Firmware Image almost exactly the same as ESP32FirmwareImage"""
|
||||
|
||||
ROM_LOADER = ESP32C5ROM
|
||||
|
||||
|
155
esptool/cmds.py
155
esptool/cmds.py
@@ -11,7 +11,6 @@ import sys
|
||||
import time
|
||||
import zlib
|
||||
import itertools
|
||||
import re
|
||||
|
||||
from intelhex import IntelHex
|
||||
from serial import SerialException
|
||||
@@ -752,20 +751,53 @@ def write_flash(esp, args):
|
||||
else:
|
||||
esp.flash_finish(False)
|
||||
|
||||
if args.verify:
|
||||
print("Verifying just-written flash...")
|
||||
print(
|
||||
"(This option is deprecated, "
|
||||
"flash contents are now always read back after flashing.)"
|
||||
)
|
||||
# If some encrypted files have been flashed,
|
||||
# print a warning saying that we won't check them
|
||||
if args.encrypt or args.encrypt_files is not None:
|
||||
print("WARNING: - cannot verify encrypted files, they will be ignored")
|
||||
# Call verify_flash function only if there is at least
|
||||
# one non-encrypted file flashed
|
||||
if not args.encrypt:
|
||||
verify_flash(esp, args)
|
||||
|
||||
def _parse_app_info(app_info_segment):
|
||||
"""
|
||||
Check if correct magic word is present in the app_info and parse the app_info struct
|
||||
"""
|
||||
app_info = app_info_segment[:256]
|
||||
# More info about the app_info struct can be found at:
|
||||
# https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/system/app_image_format.html#application-description
|
||||
APP_DESC_STRUCT_FMT = "<II" + "8s" + "32s32s16s16s32s32sHHB" + "3s" + "72s"
|
||||
(
|
||||
magic_word,
|
||||
secure_version,
|
||||
reserv1,
|
||||
version,
|
||||
project_name,
|
||||
time,
|
||||
date,
|
||||
idf_ver,
|
||||
app_elf_sha256,
|
||||
min_efuse_blk_rev_full,
|
||||
max_efuse_blk_rev_full,
|
||||
mmu_page_size,
|
||||
reserv3,
|
||||
reserv2,
|
||||
) = struct.unpack(APP_DESC_STRUCT_FMT, app_info)
|
||||
|
||||
if magic_word != 0xABCD5432:
|
||||
return None
|
||||
|
||||
return {
|
||||
"magic_word": magic_word,
|
||||
"secure_version": secure_version,
|
||||
"reserv1": reserv1,
|
||||
"version": version.decode("utf-8"),
|
||||
"project_name": project_name.decode("utf-8"),
|
||||
"time": time.decode("utf-8"),
|
||||
"date": date.decode("utf-8"),
|
||||
"idf_ver": idf_ver.decode("utf-8"),
|
||||
"app_elf_sha256": hexify(app_elf_sha256, uppercase=False),
|
||||
"min_efuse_blk_rev_full": f"{min_efuse_blk_rev_full // 100}.{min_efuse_blk_rev_full % 100}",
|
||||
"max_efuse_blk_rev_full": f"{max_efuse_blk_rev_full // 100}.{max_efuse_blk_rev_full % 100}",
|
||||
"mmu_page_size": f"{2 ** mmu_page_size // 1024} KB"
|
||||
if mmu_page_size != 0
|
||||
else None,
|
||||
"reserv3": reserv3,
|
||||
"reserv2": reserv2,
|
||||
}
|
||||
|
||||
|
||||
def image_info(args):
|
||||
@@ -872,14 +904,14 @@ def image_info(args):
|
||||
"{} {} {} {} {}".format("-" * 7, "-" * 7, "-" * 10, "-" * 10, "-" * 12)
|
||||
)
|
||||
format_str = "{:7} {:#07x} {:#010x} {:#010x} {}"
|
||||
app_desc = None
|
||||
app_desc_seg = None
|
||||
bootloader_desc = None
|
||||
for idx, seg in enumerate(image.segments):
|
||||
segs = seg.get_memory_type(image)
|
||||
seg_name = ", ".join(segs)
|
||||
# The DROM segment starts with the esp_app_desc_t struct
|
||||
if "DROM" in segs and app_desc is None:
|
||||
app_desc = seg.data[:256]
|
||||
if "DROM" in segs and app_desc_seg is None:
|
||||
app_desc_seg = seg.data[:256]
|
||||
elif "DRAM" in segs:
|
||||
# The DRAM segment starts with the esp_bootloader_desc_t struct
|
||||
if len(seg.data) >= 80:
|
||||
@@ -916,51 +948,27 @@ def image_info(args):
|
||||
except AttributeError:
|
||||
pass # ESP8266 image has no append_digest field
|
||||
|
||||
if app_desc:
|
||||
APP_DESC_STRUCT_FMT = "<II" + "8s" + "32s32s16s16s32s32sHHB" + "3s" + "72s"
|
||||
(
|
||||
magic_word,
|
||||
secure_version,
|
||||
reserv1,
|
||||
version,
|
||||
project_name,
|
||||
time,
|
||||
date,
|
||||
idf_ver,
|
||||
app_elf_sha256,
|
||||
min_efuse_blk_rev_full,
|
||||
max_efuse_blk_rev_full,
|
||||
mmu_page_size,
|
||||
reserv3,
|
||||
reserv2,
|
||||
) = struct.unpack(APP_DESC_STRUCT_FMT, app_desc)
|
||||
|
||||
if magic_word == 0xABCD5432:
|
||||
if app_desc_seg:
|
||||
app_desc = _parse_app_info(app_desc_seg)
|
||||
if app_desc:
|
||||
print()
|
||||
title = "Application information"
|
||||
print(title)
|
||||
print("=" * len(title))
|
||||
print(f'Project name: {project_name.decode("utf-8")}')
|
||||
print(f'App version: {version.decode("utf-8")}')
|
||||
print(f'Compile time: {date.decode("utf-8")} {time.decode("utf-8")}')
|
||||
print(f"ELF file SHA256: {hexify(app_elf_sha256, uppercase=False)}")
|
||||
print(f'ESP-IDF: {idf_ver.decode("utf-8")}')
|
||||
print(f'Project name: {app_desc["project_name"]}')
|
||||
print(f'App version: {app_desc["version"]}')
|
||||
print(f'Compile time: {app_desc["date"]} {app_desc["time"]}')
|
||||
print(f"ELF file SHA256: {app_desc['app_elf_sha256']}")
|
||||
print(f'ESP-IDF: {app_desc["idf_ver"]}')
|
||||
print(
|
||||
f"Minimal eFuse block revision: {min_efuse_blk_rev_full // 100}.{min_efuse_blk_rev_full % 100}"
|
||||
f"Minimal eFuse block revision: {app_desc['min_efuse_blk_rev_full']}"
|
||||
)
|
||||
print(
|
||||
f"Maximal eFuse block revision: {max_efuse_blk_rev_full // 100}.{max_efuse_blk_rev_full % 100}"
|
||||
f"Maximal eFuse block revision: {app_desc['max_efuse_blk_rev_full']}"
|
||||
)
|
||||
|
||||
# MMU page size is only available in ESP-IDF v5.4 and later
|
||||
# regex matches major and minor version numbers, idf_ver can look like "v5.4.1-dirty"
|
||||
ver = re.match(r"v(\d+)\.(\d+)", idf_ver.decode("utf-8"))
|
||||
if ver:
|
||||
major, minor = ver.groups()
|
||||
if int(major) >= 5 and int(minor) >= 4:
|
||||
print(f"MMU page size: {2 ** mmu_page_size // 1024} KB")
|
||||
|
||||
print(f"Secure version: {secure_version}")
|
||||
if app_desc["mmu_page_size"]:
|
||||
print(f"MMU page size: {app_desc['mmu_page_size']}")
|
||||
print(f"Secure version: {app_desc['secure_version']}")
|
||||
|
||||
elif bootloader_desc:
|
||||
BOOTLOADER_DESC_STRUCT_FMT = "<B" + "3s" + "I32s24s" + "16s"
|
||||
@@ -1112,6 +1120,39 @@ def elf2image(args):
|
||||
|
||||
if args.flash_mmu_page_size:
|
||||
image.set_mmu_page_size(flash_size_bytes(args.flash_mmu_page_size))
|
||||
else:
|
||||
appdesc_seg = None
|
||||
for seg in e.sections:
|
||||
if ".flash.appdesc" in seg.name:
|
||||
appdesc_seg = seg
|
||||
break
|
||||
# If ELF file contains app description segment, which is in flash memory (RAM build has it too, but does not have MMU page size) and chip has configurable MMU page size.
|
||||
if (
|
||||
appdesc_seg
|
||||
and image.is_flash_addr(appdesc_seg.addr)
|
||||
and image.MMU_PAGE_SIZE_CONF
|
||||
):
|
||||
app_desc = _parse_app_info(appdesc_seg.data)
|
||||
if app_desc:
|
||||
# MMU page size is specified in app description segment since ESP-IDF v5.4
|
||||
if app_desc["mmu_page_size"]:
|
||||
image.set_mmu_page_size(flash_size_bytes(app_desc["mmu_page_size"]))
|
||||
# Try to set the correct MMU page size based on the app description starting address which,
|
||||
# without image + extended header (24 bytes) and segment header (8 bytes), should be aligned to MMU page size.
|
||||
else:
|
||||
for mmu_page_size in reversed(image.MMU_PAGE_SIZE_CONF):
|
||||
if (appdesc_seg.addr - 24 - 8) % mmu_page_size == 0:
|
||||
image.set_mmu_page_size(mmu_page_size)
|
||||
print(
|
||||
f"MMU page size not specified, set to {image.IROM_ALIGN // 1024} KB"
|
||||
)
|
||||
break
|
||||
else:
|
||||
print(
|
||||
"Warning: App description segment is not aligned to MMU page size, "
|
||||
"probably linker script issue or wrong MMU page size. "
|
||||
"Try to set MMU page size parameter manually."
|
||||
)
|
||||
|
||||
# ELFSection is a subclass of ImageSegment, so can use interchangeably
|
||||
image.segments = e.segments if args.use_segments else e.sections
|
||||
@@ -1123,6 +1164,10 @@ def elf2image(args):
|
||||
if args.elf_sha256_offset:
|
||||
image.elf_sha256 = e.sha256()
|
||||
image.elf_sha256_offset = args.elf_sha256_offset
|
||||
# If the ELF file contains an app_desc section, put the SHA256 digest at the correct offset
|
||||
elif any(".flash.appdesc" in seg.name for seg in image.segments):
|
||||
image.elf_sha256 = e.sha256()
|
||||
image.elf_sha256_offset = 0xB0
|
||||
|
||||
if args.ram_only_header:
|
||||
print(
|
||||
|
Binary file not shown.
BIN
test/elf2image/esp32c6-appdesc.elf
Executable file
BIN
test/elf2image/esp32c6-appdesc.elf
Executable file
Binary file not shown.
18
test/elf2image/esp32c6-appdesc/Makefile
Normal file
18
test/elf2image/esp32c6-appdesc/Makefile
Normal file
@@ -0,0 +1,18 @@
|
||||
CC = riscv32-esp-elf-gcc
|
||||
CFLAGS = -Os -ffreestanding -nostdlib
|
||||
LDFLAGS = -T esp32c6-appdesc.ld
|
||||
|
||||
TARGET = esp32c6-appdesc.elf
|
||||
SRC = main.c
|
||||
OBJ = main.o
|
||||
|
||||
all: $(TARGET)
|
||||
|
||||
$(TARGET): $(OBJ)
|
||||
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c $< -o $@
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ) $(TARGET)
|
16
test/elf2image/esp32c6-appdesc/esp32c6-appdesc.ld
Normal file
16
test/elf2image/esp32c6-appdesc/esp32c6-appdesc.ld
Normal file
@@ -0,0 +1,16 @@
|
||||
MEMORY
|
||||
{
|
||||
/**
|
||||
* 0x42000000 is start of flash + 0x20 for image + extended header and segment header
|
||||
* 0x14c is length of esp_app_desc_t structure
|
||||
*/
|
||||
drom_seg (R) : org = 0x42000020, len = 0x14c
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.flash.appdesc :
|
||||
{
|
||||
KEEP(*(.flash.appdesc))
|
||||
} > drom_seg
|
||||
}
|
45
test/elf2image/esp32c6-appdesc/main.c
Normal file
45
test/elf2image/esp32c6-appdesc/main.c
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <stdint.h>
|
||||
|
||||
// This is the structure of the application description section in the binary image (taken from ESP-IDF).
|
||||
typedef struct {
|
||||
uint32_t magic_word;
|
||||
uint32_t secure_version;
|
||||
uint32_t reserv1[2];
|
||||
char version[32];
|
||||
char project_name[32];
|
||||
char time[16];
|
||||
char date[16];
|
||||
char idf_ver[32];
|
||||
uint8_t app_elf_sha256[32];
|
||||
uint16_t min_efuse_blk_rev_full;
|
||||
uint16_t max_efuse_blk_rev_full;
|
||||
uint8_t mmu_page_size;
|
||||
uint8_t reserv3[3];
|
||||
uint32_t reserv2[18];
|
||||
} esp_app_desc_t;
|
||||
|
||||
__attribute__((section(".flash.appdesc")))
|
||||
esp_app_desc_t my_app_desc = {
|
||||
.magic_word = 0xABCD5432,
|
||||
.secure_version = 0xffffffff,
|
||||
.reserv1 = {0xffffffff, 0xffffffff},
|
||||
.version = "_______________________________",
|
||||
.project_name = "-------------------------------",
|
||||
.time = "xxxxxxxxxxxxxxx",
|
||||
.date = "yyyyyyyyyyyyyyy",
|
||||
.idf_ver = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
|
||||
.app_elf_sha256 = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
},
|
||||
.min_efuse_blk_rev_full = 0xffff,
|
||||
.max_efuse_blk_rev_full = 0xffff,
|
||||
.mmu_page_size = 0,
|
||||
.reserv3 = {0xff, 0xff, 0xff},
|
||||
.reserv2 = {
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff
|
||||
},
|
||||
};
|
@@ -5,6 +5,7 @@ import re
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
import math
|
||||
|
||||
from conftest import need_to_install_package_err
|
||||
|
||||
@@ -138,7 +139,9 @@ class BaseTestCase:
|
||||
"warning" not in output.lower()
|
||||
), "Should be no warnings in image_info output"
|
||||
|
||||
def run_elf2image(self, chip, elf_path, version=None, extra_args=[]):
|
||||
def run_elf2image(
|
||||
self, chip, elf_path, version=None, extra_args=[], allow_warnings=False
|
||||
):
|
||||
"""Run elf2image on elf_path"""
|
||||
cmd = [sys.executable, "-m", "esptool", "--chip", chip, "elf2image"]
|
||||
if version is not None:
|
||||
@@ -149,9 +152,10 @@ class BaseTestCase:
|
||||
output = subprocess.check_output(cmd)
|
||||
output = output.decode("utf-8")
|
||||
print(output)
|
||||
assert (
|
||||
"warning" not in output.lower()
|
||||
), "elf2image should not output warnings"
|
||||
if not allow_warnings:
|
||||
assert (
|
||||
"warning" not in output.lower()
|
||||
), "elf2image should not output warnings"
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(e.output)
|
||||
raise
|
||||
@@ -390,15 +394,16 @@ class TestESP32FlashHeader(BaseTestCase):
|
||||
|
||||
|
||||
class TestELFSHA256(BaseTestCase):
|
||||
ELF = "esp32-app-cust-ver-info.elf"
|
||||
ELF = "esp32c6-appdesc.elf"
|
||||
SHA_OFFS = 0xB0 # absolute offset of the SHA in the .bin file
|
||||
BIN = "esp32-app-cust-ver-info.bin"
|
||||
BIN = "esp32c6-appdesc.bin"
|
||||
|
||||
"""
|
||||
esp32-app-cust-ver-info.elf was built with the following application version info:
|
||||
esp32c6-appdesc.elf was built with the following application version info:
|
||||
|
||||
const __attribute__((section(".rodata_desc"))) esp_app_desc_t esp_app_desc = {
|
||||
.magic_word = 0xffffffff,
|
||||
__attribute__((section(".flash.appdesc")))
|
||||
esp_app_desc_t my_app_desc = {
|
||||
.magic_word = 0xABCD5432,
|
||||
.secure_version = 0xffffffff,
|
||||
.reserv1 = {0xffffffff, 0xffffffff},
|
||||
.version = "_______________________________",
|
||||
@@ -406,21 +411,28 @@ class TestELFSHA256(BaseTestCase):
|
||||
.time = "xxxxxxxxxxxxxxx",
|
||||
.date = "yyyyyyyyyyyyyyy",
|
||||
.idf_ver = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz",
|
||||
.app_elf_sha256 =
|
||||
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||
.reserv2 = {0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff},
|
||||
.app_elf_sha256 = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
},
|
||||
.min_efuse_blk_rev_full = 0xffff,
|
||||
.max_efuse_blk_rev_full = 0xffff,
|
||||
.mmu_page_size = 0,
|
||||
.reserv3 = {0xff, 0xff, 0xff},
|
||||
.reserv2 = {
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
|
||||
0xffffffff, 0xffffffff, 0xffffffff
|
||||
},
|
||||
};
|
||||
|
||||
This leaves zeroes only for the fields of SHA-256 and the test will fail
|
||||
if the placement of zeroes are tested at the wrong place.
|
||||
|
||||
00000000: e907 0020 780f 0840 ee00 0000 0000 0000 ... x..@........
|
||||
00000010: 0000 0000 0000 0001 2000 403f 605a 0000 ........ .@?`Z..
|
||||
00000020: ffff ffff ffff ffff ffff ffff ffff ffff ................
|
||||
00000000: e901 0000 2000 0042 ee00 0000 0d00 0000 .... ..B........
|
||||
00000010: 00ff ff00 0000 0001 2000 0042 0001 0000 ........ ..B....
|
||||
00000020: 3254 cdab ffff ffff ffff ffff ffff ffff 2T..............
|
||||
00000030: 5f5f 5f5f 5f5f 5f5f 5f5f 5f5f 5f5f 5f5f ________________
|
||||
00000040: 5f5f 5f5f 5f5f 5f5f 5f5f 5f5f 5f5f 5f00 _______________.
|
||||
00000050: 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d 2d2d ----------------
|
||||
@@ -431,37 +443,46 @@ class TestELFSHA256(BaseTestCase):
|
||||
000000a0: 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a7a 7a00 zzzzzzzzzzzzzzz.
|
||||
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ SHA-256 here
|
||||
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................
|
||||
000000d0: ffff ffff ffff ffff ffff ffff ffff ffff ................
|
||||
000000d0: ffff ffff 00ff ffff ffff ffff ffff ffff ................
|
||||
000000e0: ffff ffff ffff ffff ffff ffff ffff ffff ................
|
||||
000000f0: ffff ffff ffff ffff ffff ffff ffff ffff ................
|
||||
00000100: ffff ffff ffff ffff ffff ffff ffff ffff ................
|
||||
00000110: ffff ffff ffff ffff ffff ffff ffff ffff ................
|
||||
00000120: 6370 755f 7374 6172 7400 0000 1b5b 303b cpu_start....[0;
|
||||
|
||||
"""
|
||||
|
||||
def test_binary_patched(self):
|
||||
def verify_sha256(self, elf_path, bin_path):
|
||||
image = esptool.bin_image.LoadFirmwareImage("esp32c6", bin_path)
|
||||
rodata_segment = image.segments[0]
|
||||
bin_sha256 = rodata_segment.data[
|
||||
self.SHA_OFFS - 0x20 : self.SHA_OFFS - 0x20 + 32
|
||||
] # subtract 0x20 byte header here
|
||||
|
||||
with open(elf_path, "rb") as f:
|
||||
elf_computed_sha256 = hashlib.sha256(f.read()).digest()
|
||||
|
||||
with open(bin_path, "rb") as f:
|
||||
f.seek(self.SHA_OFFS)
|
||||
bin_sha256_raw = f.read(len(elf_computed_sha256))
|
||||
|
||||
assert elf_computed_sha256 == bin_sha256
|
||||
assert elf_computed_sha256 == bin_sha256_raw
|
||||
|
||||
def test_binary_patched_parameter(self):
|
||||
try:
|
||||
self.run_elf2image(
|
||||
"esp32",
|
||||
"esp32c6",
|
||||
self.ELF,
|
||||
extra_args=["--elf-sha256-offset", f"{self.SHA_OFFS:#x}"],
|
||||
)
|
||||
image = esptool.bin_image.LoadFirmwareImage("esp32", self.BIN)
|
||||
rodata_segment = image.segments[0]
|
||||
bin_sha256 = rodata_segment.data[
|
||||
self.SHA_OFFS - 0x20 : self.SHA_OFFS - 0x20 + 32
|
||||
] # subtract 0x20 byte header here
|
||||
self.verify_sha256(self.ELF, self.BIN)
|
||||
finally:
|
||||
try_delete(self.BIN)
|
||||
|
||||
with open(self.ELF, "rb") as f:
|
||||
elf_computed_sha256 = hashlib.sha256(f.read()).digest()
|
||||
|
||||
with open(self.BIN, "rb") as f:
|
||||
f.seek(self.SHA_OFFS)
|
||||
bin_sha256_raw = f.read(len(elf_computed_sha256))
|
||||
|
||||
assert elf_computed_sha256 == bin_sha256
|
||||
assert elf_computed_sha256 == bin_sha256_raw
|
||||
def test_binary_patched(self):
|
||||
try:
|
||||
self.run_elf2image("esp32c6", self.ELF)
|
||||
self.verify_sha256(self.ELF, self.BIN)
|
||||
finally:
|
||||
try_delete(self.BIN)
|
||||
|
||||
@@ -516,3 +537,96 @@ class TestHashAppend(BaseTestCase):
|
||||
|
||||
assert bin_without_hash[self.HASH_APPEND_OFFSET] == 0
|
||||
assert bytes(expected_bin_without_hash) == bin_without_hash
|
||||
|
||||
|
||||
class TestMMUPageSize(BaseTestCase):
|
||||
def test_appdesc_aligned(self, capsys):
|
||||
ELF = "esp32c6-appdesc.elf"
|
||||
BIN = "esp32c6-appdesc.bin"
|
||||
try:
|
||||
self.run_elf2image("esp32c6", ELF)
|
||||
output = capsys.readouterr().out
|
||||
print(output)
|
||||
assert "MMU page size not specified, set to 64 KB" in output
|
||||
finally:
|
||||
try_delete(BIN)
|
||||
|
||||
@staticmethod
|
||||
def _modify_section_address(elf_path, section_name, address_offset):
|
||||
with open(elf_path, "rb+") as f:
|
||||
elf = ELFFile(f)
|
||||
section = elf.get_section_by_name(section_name)
|
||||
|
||||
if not section:
|
||||
raise ValueError(f"Section {section_name} not found")
|
||||
|
||||
# This finds the index of the specified section in the ELF file,
|
||||
# compute the section header’s position in the file (using the section index,
|
||||
# the section header table’s offset and section header entry size) and then
|
||||
# modify the section’s address in memory by the specified offset.
|
||||
index = elf.get_section_index(section_name)
|
||||
sh_entry_offset = elf.header["e_shoff"] + index * elf.header["e_shentsize"]
|
||||
section.header.sh_addr += address_offset
|
||||
|
||||
# Write modified header to file
|
||||
f.seek(sh_entry_offset)
|
||||
f.write(elf.structs.Elf_Shdr.build(section.header))
|
||||
|
||||
def test_appdesc_not_aligned(self, capsys):
|
||||
ELF = "esp32c6-appdesc.elf"
|
||||
BIN = "esp32c6-appdesc.bin"
|
||||
ADDRESS_OFFSET = 4 # 4 bytes is minimum allowed
|
||||
|
||||
self._modify_section_address(ELF, ".flash.appdesc", ADDRESS_OFFSET)
|
||||
try:
|
||||
self.run_elf2image("esp32c6", ELF, allow_warnings=True)
|
||||
output = capsys.readouterr().out
|
||||
print(output)
|
||||
assert (
|
||||
"App description segment is not aligned to MMU page size, probably linker script issue or wrong MMU page size. Use --flash-mmu-page-size to set it manually."
|
||||
in output
|
||||
)
|
||||
finally:
|
||||
# Restore original address to be able to run other tests
|
||||
self._modify_section_address(ELF, ".flash.appdesc", -ADDRESS_OFFSET)
|
||||
try_delete(BIN)
|
||||
|
||||
@staticmethod
|
||||
def _change_appdesc_mmu_page_size(elf_path, mmu_page_size):
|
||||
"""
|
||||
Change the MMU page size in the appdesc section of the ELF file.
|
||||
The following values can be chosen: 0 (empty), 8192, 16384, 32768, 65536.
|
||||
The numbers are not valid for all chips, so refer to the documentation of the chip being used.
|
||||
"""
|
||||
with open(elf_path, "rb+") as f:
|
||||
elf = ELFFile(f)
|
||||
section = elf.get_section_by_name(".flash.appdesc")
|
||||
|
||||
if not section:
|
||||
raise ValueError("Section .flash.appdesc not found")
|
||||
|
||||
# The mmu_page_size is a power of 2, so we need to convert it to the corresponding
|
||||
# value that should be written in the appdesc section. It can also be empty (0).
|
||||
if mmu_page_size == 0:
|
||||
mmu_page_size_appdesc = 0
|
||||
else:
|
||||
mmu_page_size_appdesc = int(math.log2(mmu_page_size))
|
||||
|
||||
# Go to the mmu_page_size in the appdesc section (which is at offset 0xB4) and change it
|
||||
f.seek(section.header.sh_offset + 0xB4)
|
||||
f.write(mmu_page_size_appdesc.to_bytes(4, byteorder="little"))
|
||||
|
||||
def test_appdesc_data(self, capsys):
|
||||
ELF = "esp32c6-appdesc.elf"
|
||||
BIN = "esp32c6-appdesc.bin"
|
||||
MMU_PAGE_SIZE = 65536
|
||||
|
||||
self._change_appdesc_mmu_page_size(ELF, MMU_PAGE_SIZE)
|
||||
try:
|
||||
self.run_elf2image("esp32c6", ELF)
|
||||
output = capsys.readouterr().out
|
||||
assert "MMU page size" not in output
|
||||
print(output)
|
||||
finally:
|
||||
self._change_appdesc_mmu_page_size(ELF, 0)
|
||||
try_delete(BIN)
|
||||
|
Reference in New Issue
Block a user