feat(partition_table): Support recovery bootloader subtype

This commit is contained in:
Konstantin Kondrashov 2024-07-03 17:28:12 +03:00 committed by BOT
parent 1689c7e14f
commit 52f14f344d
10 changed files with 50 additions and 8 deletions

View File

@ -32,6 +32,7 @@ extern "C" {
#define PART_TYPE_BOOTLOADER 0x02
#define PART_SUBTYPE_BOOTLOADER_PRIMARY 0x00
#define PART_SUBTYPE_BOOTLOADER_OTA 0x01
#define PART_SUBTYPE_BOOTLOADER_RECOVERY 0x02
#define PART_TYPE_PARTITION_TABLE 0x03
#define PART_SUBTYPE_PARTITION_TABLE_PRIMARY 0x00

View File

@ -208,6 +208,9 @@ bool bootloader_utility_load_partition_table(bootloader_state_t *bs)
case PART_SUBTYPE_BOOTLOADER_OTA:
partition_usage = "ota bootloader";
break;
case PART_SUBTYPE_BOOTLOADER_RECOVERY:
partition_usage = "recovery bootloader";
break;
}
break; /* PART_TYPE_BOOTLOADER */
case PART_TYPE_PARTITION_TABLE: /* Partition table partition */

View File

@ -69,6 +69,7 @@ typedef enum {
typedef enum {
ESP_PARTITION_SUBTYPE_BOOTLOADER_PRIMARY = 0x00, //!< Primary Bootloader
ESP_PARTITION_SUBTYPE_BOOTLOADER_OTA = 0x01, //!< Temporary OTA storage for Bootloader, where the OTA uploads a new Bootloader image
ESP_PARTITION_SUBTYPE_BOOTLOADER_RECOVERY = 0x02, //!< Recovery Bootloader
ESP_PARTITION_SUBTYPE_PARTITION_TABLE_PRIMARY = 0x00, //!< Primary Partition table
ESP_PARTITION_SUBTYPE_PARTITION_TABLE_OTA = 0x01, //!< Temporary OTA storage for Partition table, where the OTA uploads a new Partition table image

View File

@ -80,6 +80,7 @@ const char *esp_partition_subtype_to_str(const uint32_t type, const uint32_t sub
switch (subtype) {
case PART_SUBTYPE_BOOTLOADER_PRIMARY: return "primary";
case PART_SUBTYPE_BOOTLOADER_OTA: return "ota";
case PART_SUBTYPE_BOOTLOADER_RECOVERY: return "recovery";
default: return "unknown";
}
case PART_TYPE_PARTITION_TABLE:

View File

@ -32,6 +32,10 @@ else()
set(final_partition_target "build_partition_table")
endif()
if(CONFIG_BOOTLOADER_RECOVERY_OFFSET)
set(recovery_bootloader_option --recovery-bootloader-offset ${CONFIG_BOOTLOADER_RECOVERY_OFFSET})
endif()
if(NOT CONFIG_PARTITION_TABLE_MD5)
set(md5_opt --disable-md5sum)
endif()
@ -67,6 +71,7 @@ set(gen_partition_table "${python}" "${CMAKE_CURRENT_SOURCE_DIR}/gen_esp32part.p
"-q"
"--offset" "${PARTITION_TABLE_OFFSET}"
"--primary-bootloader-offset" "${BOOTLOADER_OFFSET}"
"${recovery_bootloader_option}"
"${md5_opt}"
"${flashsize_opt}"
"${partition_secure_opt}" ${extra_partition_subtypes} "--")

View File

@ -29,7 +29,7 @@ SECURE_NONE = None
SECURE_V1 = 'v1'
SECURE_V2 = 'v2'
__version__ = '1.3'
__version__ = '1.4'
APP_TYPE = 0x00
DATA_TYPE = 0x01
@ -60,6 +60,7 @@ SUBTYPES = {
BOOTLOADER_TYPE: {
'primary': 0x00,
'ota': 0x01,
'recovery': 0x02,
},
PARTITION_TABLE_TYPE: {
'primary': 0x00,
@ -154,6 +155,7 @@ md5sum = True
secure = SECURE_NONE
offset_part_table = 0
primary_bootloader_offset = None
recovery_bootloader_offset = None
def status(msg):
@ -464,10 +466,15 @@ class PartitionDefinition(object):
return parse_int(strval)
def parse_address(self, strval, ptype, psubtype):
if ptype == BOOTLOADER_TYPE and psubtype == SUBTYPES[ptype]['primary']:
if primary_bootloader_offset is None:
raise InputError(f'Primary bootloader offset is not defined. Please use --primary-bootloader-offset')
return primary_bootloader_offset
if ptype == BOOTLOADER_TYPE:
if psubtype == SUBTYPES[ptype]['primary']:
if primary_bootloader_offset is None:
raise InputError(f'Primary bootloader offset is not defined. Please use --primary-bootloader-offset')
return primary_bootloader_offset
if psubtype == SUBTYPES[ptype]['recovery']:
if recovery_bootloader_offset is None:
raise InputError(f'Recovery bootloader offset is not defined. Please use --recovery-bootloader-offset')
return recovery_bootloader_offset
if ptype == PARTITION_TABLE_TYPE and psubtype == SUBTYPES[ptype]['primary']:
return offset_part_table
if strval == '':
@ -590,6 +597,7 @@ def main():
global offset_part_table
global secure
global primary_bootloader_offset
global recovery_bootloader_offset
parser = argparse.ArgumentParser(description='ESP32 partition table utility')
parser.add_argument('--flash-size', help='Optional flash size limit, checks partition table fits in flash',
@ -601,6 +609,7 @@ def main():
parser.add_argument('--quiet', '-q', help="Don't print non-critical status messages to stderr", action='store_true')
parser.add_argument('--offset', '-o', help='Set offset partition table', default='0x8000')
parser.add_argument('--primary-bootloader-offset', help='Set primary bootloader offset', default=None)
parser.add_argument('--recovery-bootloader-offset', help='Set recovery bootloader offset', default=None)
parser.add_argument('--secure', help='Require app partitions to be suitable for secure boot', nargs='?', const=SECURE_V1, choices=[SECURE_V1, SECURE_V2])
parser.add_argument('--extra-partition-subtypes', help='Extra partition subtype entries', nargs='*')
parser.add_argument('input', help='Path to CSV or binary file to parse.', type=argparse.FileType('rb'))
@ -620,6 +629,8 @@ def main():
f'Unsupported configuration. Primary bootloader must be below partition table. '
f'Check --primary-bootloader-offset={primary_bootloader_offset:#x} and --offset={offset_part_table:#x}'
)
if args.recovery_bootloader_offset is not None:
recovery_bootloader_offset = int(args.recovery_bootloader_offset, 0)
if args.extra_partition_subtypes:
add_extra_subtypes(args.extra_partition_subtypes)

View File

@ -14,7 +14,7 @@ import tempfile
import gen_esp32part as gen
__version__ = '2.1'
__version__ = '2.2'
COMPONENTS_PATH = os.path.expandvars(os.path.join('$IDF_PATH', 'components'))
ESPTOOL_PY = os.path.join(COMPONENTS_PATH, 'esptool_py', 'esptool', 'esptool.py')
@ -56,13 +56,14 @@ PARTITION_BOOT_DEFAULT = _PartitionId()
class ParttoolTarget():
def __init__(self, port=None, baud=None, partition_table_offset=PARTITION_TABLE_OFFSET, primary_bootloader_offset=None, partition_table_file=None,
esptool_args=[], esptool_write_args=[], esptool_read_args=[], esptool_erase_args=[]):
def __init__(self, port=None, baud=None, partition_table_offset=PARTITION_TABLE_OFFSET, primary_bootloader_offset=None, recovery_bootloader_offset=None,
partition_table_file=None, esptool_args=[], esptool_write_args=[], esptool_read_args=[], esptool_erase_args=[]):
self.port = port
self.baud = baud
gen.offset_part_table = partition_table_offset
gen.primary_bootloader_offset = primary_bootloader_offset
gen.recovery_bootloader_offset = recovery_bootloader_offset
def parse_esptool_args(esptool_args):
results = list()
@ -241,6 +242,7 @@ def main():
parser.add_argument('--partition-table-offset', '-o', help='offset to read the partition table from', type=str)
parser.add_argument('--primary-bootloader-offset', help='offset for primary bootloader', type=str)
parser.add_argument('--recovery-bootloader-offset', help='offset for recovery bootloader', type=str)
parser.add_argument('--partition-table-file', '-f', help='file (CSV/binary) to read the partition table from; \
overrides device attached to specified port as the partition table source when defined')
@ -318,6 +320,9 @@ def main():
if args.primary_bootloader_offset:
target_args['primary_bootloader_offset'] = int(args.primary_bootloader_offset, 0)
if args.recovery_bootloader_offset:
target_args['recovery_bootloader_offset'] = int(args.recovery_bootloader_offset, 0)
if args.esptool_args:
target_args['esptool_args'] = args.esptool_args

View File

@ -7,6 +7,12 @@ if(NOT DEFINED BOOTLOADER_OFFSET) # For Linux target
endif()
endif()
if(CONFIG_BOOTLOADER_RECOVERY_OFFSET)
set(RECOVERY_BOOTLOADER_OPTION --recovery-bootloader-offset ${CONFIG_BOOTLOADER_RECOVERY_OFFSET})
else()
set(RECOVERY_BOOTLOADER_OPTION "")
endif()
set(PARTITION_TABLE_CHECK_SIZES_TOOL_PATH "${CMAKE_CURRENT_LIST_DIR}/check_sizes.py")
idf_build_get_property(build_dir BUILD_DIR)
@ -66,6 +72,7 @@ function(partition_table_get_partition_info result get_part_info_args part_info)
${idf_path}/components/partition_table/parttool.py -q
--partition-table-offset ${PARTITION_TABLE_OFFSET}
--primary-bootloader-offset ${BOOTLOADER_OFFSET}
${RECOVERY_BOOTLOADER_OPTION}
--partition-table-file ${PARTITION_CSV_PATH}
get_partition_info ${get_part_info_args} --info ${part_info}
${extra_partition_subtypes}

View File

@ -64,6 +64,7 @@ def _strip_trailing_ffs(binary_table):
class CSVParserTests(Py23TestCase):
def tearDown(self):
gen_esp32part.primary_bootloader_offset = None
gen_esp32part.recovery_bootloader_offset = None
gen_esp32part.offset_part_table = 0
def test_simple_partition(self):
@ -197,8 +198,10 @@ partition_table, partition_table, primary, N/A, N/A
FactoryApp, app, factory, , 1M
OtaBTLDR, bootloader, ota, , N/A
OtaPrtTable, partition_table, ota, , N/A
RecoveryBTLDR, bootloader, recovery, N/A, N/A
"""
gen_esp32part.primary_bootloader_offset = 0x1000
gen_esp32part.recovery_bootloader_offset = 0x200000
gen_esp32part.offset_part_table = 0x9000
part_table_size = 0x1000
bootloader_size = gen_esp32part.offset_part_table - gen_esp32part.primary_bootloader_offset
@ -218,6 +221,9 @@ OtaPrtTable, partition_table, ota, , N/A
# OtaPrtTable
self.assertEqual(t[4].offset, 0x118000)
self.assertEqual(t[4].size, part_table_size)
# RecoveryBTLDR
self.assertEqual(t[5].offset, gen_esp32part.recovery_bootloader_offset)
self.assertEqual(t[5].size, bootloader_size)
class BinaryOutputTests(Py23TestCase):

View File

@ -84,6 +84,7 @@ Here is an example of a CSV partition table that includes bootloader and partiti
nvs, data, nvs, , 0x6000,
phy_init, data, phy, , 0x1000,
factory, app, factory, , 1M,
recoveryBloader, bootloader, recovery, N/A, N/A,
The ``gen_esp32part.py`` tool will replace each ``N/A`` with appropriate values based on the selected Kconfig options: {IDF_TARGET_CONFIG_BOOTLOADER_OFFSET_IN_FLASH} for the bootloader offset and :ref:`CONFIG_PARTITION_TABLE_OFFSET` for the partition table offset.
@ -136,6 +137,7 @@ See enum :cpp:type:`esp_partition_subtype_t` for the full list of subtypes defin
- ``primary`` (0x00). This is the 2nd stage bootloader, located at the {IDF_TARGET_CONFIG_BOOTLOADER_OFFSET_IN_FLASH} address in flash memory. The tool automatically determines the appropriate size and offset for this subtype, so any size or offset specified for this subtype will be ignored. You can either leave these fields blank or use ``N/A`` as a placeholder.
- ``ota`` (0x01). This is a temporary bootloader partition used by the bootloader OTA update functionality to download a new image. The tool ignores the size for this subtype, allowing you to leave it blank or use ``N/A``. You can only specify an offset, or leave it blank to have the tool calculate it based on the offsets of previously used partitions.
- ``recovery`` (0x02). This is the recovery bootloader partition used for safely performing OTA updates to the bootloader. The ``gen_esp32part.py`` tool automatically determines the address and size for this partition, so you can leave these fields blank or use ``N/A`` as a placeholder. The address must match an eFuse field, which is defined through a Kconfig option. If the normal bootloader loading path fails, the ROM bootloader will attempt to load the recovery partition at the address specified by the eFuse field.
The size of the bootloader type is calculated by the ``gen_esp32part.py`` tool based on the specified ``--offset`` (the partition table offset) and ``--primary-partition-offset`` arguments. Specifically, the bootloader size is defined as (:ref:`CONFIG_PARTITION_TABLE_OFFSET` - {IDF_TARGET_CONFIG_BOOTLOADER_OFFSET_IN_FLASH}). This calculated size applies to all subtypes of the bootloader.