mirror of
https://github.com/espressif/esp-idf.git
synced 2026-07-17 00:13:06 +03:00
- Remove the invalid check that compared the TEE app partition size against the user application (REE) binary size
175 lines
6.7 KiB
Python
Executable File
175 lines
6.7 KiB
Python
Executable File
#!/usr/bin/env python
|
|
#
|
|
# check_sizes.py is a tool run by the ESP-IDF build system
|
|
# to check a particular binary fits in the available partitions of
|
|
# a particular type/subtype. Can be used to check if the app binary fits in
|
|
# all available app partitions, for example.
|
|
#
|
|
# (Can also check if the bootloader binary fits before the partition table.)
|
|
#
|
|
# SPDX-FileCopyrightText: 2020-2026 Espressif Systems (Shanghai) CO LTD
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
import io # noqa: F401 # pylint: disable=unused-import
|
|
import os
|
|
|
|
try:
|
|
from typing import IO # noqa: F401 # pylint: disable=unused-import
|
|
except ImportError:
|
|
pass # used for type hinting only
|
|
|
|
import gen_esp32part
|
|
import rich_click as click
|
|
from esp_pylib.cli_types import AnyIntType
|
|
from esp_pylib.logger import log
|
|
from gen_esp32part import APP_TYPE
|
|
from gen_esp32part import MIN_PARTITION_SUBTYPE_APP_TEE
|
|
from gen_esp32part import NUM_PARTITION_SUBTYPE_APP_TEE
|
|
from gen_esp32part import PartitionTable
|
|
from gen_esp32part import get_ptype_as_int
|
|
from gen_esp32part import get_subtype_as_int
|
|
|
|
allow_failures = False
|
|
|
|
|
|
def _file_size(f): # type: (IO) -> int
|
|
before = f.tell()
|
|
f.seek(0, 2) # seek to end
|
|
result = f.tell()
|
|
f.seek(before)
|
|
return result
|
|
|
|
|
|
def _fail(msg): # type: (str) -> None
|
|
if allow_failures:
|
|
log.warn(msg)
|
|
else:
|
|
log.err(msg)
|
|
raise SystemExit(msg)
|
|
|
|
|
|
def check_bootloader(partition_table_offset, bootloader_offset, binary_file): # type: (int, int, IO) -> None
|
|
max_size = partition_table_offset - bootloader_offset
|
|
bootloader_size = _file_size(binary_file)
|
|
if bootloader_size > max_size:
|
|
msg = (
|
|
'Bootloader binary size {:#x} bytes is too large for partition table offset {:#02x}. '
|
|
+ 'Bootloader binary can be maximum {:#x} ({}) bytes unless the partition table offset '
|
|
+ 'is increased in the Partition Table section of the project configuration menu.'
|
|
).format(bootloader_size, partition_table_offset, max_size, max_size)
|
|
_fail(msg)
|
|
free_size = max_size - bootloader_size
|
|
pct = round(free_size * 100 / max_size)
|
|
log.print(f'Bootloader binary size {bootloader_size:#x} bytes. {free_size:#x} bytes ({pct}%) free.')
|
|
|
|
|
|
def check_partition(ptype, subtype, partition_table_file, bin_file): # type: (str, str, io.IOBase, IO) -> None
|
|
table, _ = PartitionTable.from_file(partition_table_file)
|
|
ptype_str = str(ptype)
|
|
ptype = get_ptype_as_int(ptype)
|
|
partitions = [p for p in table if p.type == ptype]
|
|
tee_subtypes = range(MIN_PARTITION_SUBTYPE_APP_TEE, MIN_PARTITION_SUBTYPE_APP_TEE + NUM_PARTITION_SUBTYPE_APP_TEE)
|
|
|
|
if subtype is not None:
|
|
ptype_str += f' ({subtype})'
|
|
subtype = get_subtype_as_int(ptype, subtype)
|
|
partitions = [p for p in partitions if p.subtype == subtype]
|
|
elif ptype == APP_TYPE:
|
|
partitions = [p for p in partitions if p.subtype not in tee_subtypes]
|
|
|
|
if len(partitions) == 0:
|
|
log.warn(f'Partition table does not contain any partitions matching {ptype_str}')
|
|
return
|
|
|
|
bin_name = os.path.basename(bin_file.name)
|
|
bin_size = _file_size(bin_file)
|
|
smallest_size = min(p.size for p in partitions)
|
|
if smallest_size >= bin_size:
|
|
free_size = smallest_size - bin_size
|
|
free_size_relative = free_size / smallest_size
|
|
log.print(
|
|
f'{bin_name} binary size {bin_size:#x} bytes. Smallest {ptype_str} partition is '
|
|
f'{smallest_size:#x} bytes. {free_size:#x} bytes ({free_size_relative:.0%}) free.'
|
|
)
|
|
free_size_relative_critical = 0.05
|
|
if free_size_relative < free_size_relative_critical:
|
|
log.warn(f'The smallest {ptype_str} partition is nearly full ({free_size_relative:.0%} free space left)!')
|
|
return
|
|
|
|
too_small_partitions = [p for p in partitions if p.size < bin_size]
|
|
if len(partitions) == 1:
|
|
msg = f'{ptype_str} partition is'
|
|
elif len(partitions) == len(too_small_partitions):
|
|
msg = f'All {ptype_str} partitions are'
|
|
else:
|
|
msg = f'{len(too_small_partitions)}/{len(partitions)} {ptype_str} partitions are'
|
|
msg += f' too small for binary {bin_name} size {bin_size:#x}:'
|
|
for p in too_small_partitions:
|
|
msg += f'\n - {p} (overflow {bin_size - p.size:#x})'
|
|
if not allow_failures and len(partitions) == len(too_small_partitions):
|
|
# if some partitions can fit the binary then just print a warning
|
|
_fail(msg)
|
|
else:
|
|
log.warn(msg)
|
|
|
|
|
|
def _configure_globals(offset: int, allow_failures_flag: bool) -> None:
|
|
global allow_failures # pylint: disable=global-statement
|
|
|
|
gen_esp32part.quiet = True
|
|
gen_esp32part.offset_part_table = offset
|
|
allow_failures = allow_failures_flag
|
|
|
|
|
|
@click.group(
|
|
context_settings={'help_option_names': ['-h', '--help']},
|
|
help='Check binary sizes against partition table entries',
|
|
)
|
|
@click.option('--target', type=click.Choice(['esp32', 'esp32s2']))
|
|
@click.option(
|
|
'--allow-failures',
|
|
'allow_failures_flag',
|
|
is_flag=True,
|
|
help='If true, failures will print warnings but not exit with an error',
|
|
)
|
|
@click.option('--offset', '-o', type=AnyIntType(), default=0x8000, show_default=True, help='Set partition table offset')
|
|
@click.pass_context
|
|
def cli(ctx, target, allow_failures_flag, offset): # type: ignore[no-untyped-def]
|
|
ctx.ensure_object(dict)
|
|
ctx.obj['offset'] = offset
|
|
ctx.obj['allow_failures'] = allow_failures_flag
|
|
|
|
|
|
@cli.command('bootloader', help='Check bootloader binary size against partition table offset')
|
|
@click.argument('bootloader_offset', type=AnyIntType())
|
|
@click.argument('bootloader_binary', type=click.File('rb'))
|
|
@click.pass_context
|
|
def bootloader_cmd(ctx, bootloader_offset, bootloader_binary): # type: ignore[no-untyped-def]
|
|
_configure_globals(ctx.obj['offset'], ctx.obj['allow_failures'])
|
|
check_bootloader(ctx.obj['offset'], bootloader_offset, bootloader_binary)
|
|
|
|
|
|
@cli.command('partition', help='Check binary size against partition table entries')
|
|
@click.option(
|
|
'--type', 'partition_type', required=True, help='Check the file size against all partitions of this type.'
|
|
)
|
|
@click.option('--subtype', help='Optional, only check the file size against all partitions of this subtype.')
|
|
@click.argument('partition_table', type=click.File('rb'))
|
|
@click.argument('binary', type=click.File('rb'))
|
|
@click.pass_context
|
|
def partition_cmd(ctx, partition_type, subtype, partition_table, binary): # type: ignore[no-untyped-def]
|
|
_configure_globals(ctx.obj['offset'], ctx.obj['allow_failures'])
|
|
check_partition(partition_type, subtype, partition_table, binary)
|
|
|
|
|
|
def main(): # type: () -> None
|
|
cli()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
from esp_pylib.excepthook import install_exception_reporting
|
|
|
|
install_exception_reporting()
|
|
main()
|