Skip to content

Commit

Permalink
fix(elf2image): Try to correct MMU page size if not specified
Browse files Browse the repository at this point in the history
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 #1062
  • Loading branch information
Dzarda7 committed Feb 17, 2025
1 parent 447de60 commit d9afa9c
Show file tree
Hide file tree
Showing 8 changed files with 344 additions and 101 deletions.
23 changes: 14 additions & 9 deletions esptool/bin_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import re
import struct
import tempfile
from typing import IO, Optional
from typing import IO, Optional, Tuple

from intelhex import HexRecordError, IntelHex

Expand Down Expand Up @@ -180,6 +180,7 @@ def __repr__(self):
class BaseFirmwareImage(object):
SEG_HEADER_LEN = 8
SHA256_DIGEST_LEN = 32
MMU_PAGE_SIZE_CONF: Tuple[int, ...] = ()

""" Base class with common firmware image functions """

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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

Expand All @@ -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

Expand Down
155 changes: 100 additions & 55 deletions esptool/cmds.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import time
import zlib
import itertools
import re

from intelhex import IntelHex
from serial import SerialException
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -872,14 +904,14 @@ def get_key_from_value(dict, val):
"{} {} {} {} {}".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:
Expand Down Expand Up @@ -916,51 +948,27 @@ def get_key_from_value(dict, val):
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"
Expand Down Expand Up @@ -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
Expand All @@ -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(
Expand Down
Binary file removed test/elf2image/esp32-app-cust-ver-info.elf
Binary file not shown.
Binary file added test/elf2image/esp32c6-appdesc.elf
Binary file not shown.
18 changes: 18 additions & 0 deletions test/elf2image/esp32c6-appdesc/Makefile
Original file line number Diff line number Diff line change
@@ -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 changes: 16 additions & 0 deletions test/elf2image/esp32c6-appdesc/esp32c6-appdesc.ld
Original file line number Diff line number Diff line change
@@ -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 changes: 45 additions & 0 deletions test/elf2image/esp32c6-appdesc/main.c
Original file line number Diff line number Diff line change
@@ -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
},
};
Loading

0 comments on commit d9afa9c

Please sign in to comment.