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_TYPE_BOOTLOADER 0x02
#define PART_SUBTYPE_BOOTLOADER_PRIMARY 0x00 #define PART_SUBTYPE_BOOTLOADER_PRIMARY 0x00
#define PART_SUBTYPE_BOOTLOADER_OTA 0x01 #define PART_SUBTYPE_BOOTLOADER_OTA 0x01
#define PART_SUBTYPE_BOOTLOADER_RECOVERY 0x02
#define PART_TYPE_PARTITION_TABLE 0x03 #define PART_TYPE_PARTITION_TABLE 0x03
#define PART_SUBTYPE_PARTITION_TABLE_PRIMARY 0x00 #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: case PART_SUBTYPE_BOOTLOADER_OTA:
partition_usage = "ota bootloader"; partition_usage = "ota bootloader";
break; break;
case PART_SUBTYPE_BOOTLOADER_RECOVERY:
partition_usage = "recovery bootloader";
break;
} }
break; /* PART_TYPE_BOOTLOADER */ break; /* PART_TYPE_BOOTLOADER */
case PART_TYPE_PARTITION_TABLE: /* Partition table partition */ case PART_TYPE_PARTITION_TABLE: /* Partition table partition */

View File

@@ -69,6 +69,7 @@ typedef enum {
typedef enum { typedef enum {
ESP_PARTITION_SUBTYPE_BOOTLOADER_PRIMARY = 0x00, //!< Primary Bootloader 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_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_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 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) { switch (subtype) {
case PART_SUBTYPE_BOOTLOADER_PRIMARY: return "primary"; case PART_SUBTYPE_BOOTLOADER_PRIMARY: return "primary";
case PART_SUBTYPE_BOOTLOADER_OTA: return "ota"; case PART_SUBTYPE_BOOTLOADER_OTA: return "ota";
case PART_SUBTYPE_BOOTLOADER_RECOVERY: return "recovery";
default: return "unknown"; default: return "unknown";
} }
case PART_TYPE_PARTITION_TABLE: case PART_TYPE_PARTITION_TABLE:

View File

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

View File

@@ -29,7 +29,7 @@ SECURE_NONE = None
SECURE_V1 = 'v1' SECURE_V1 = 'v1'
SECURE_V2 = 'v2' SECURE_V2 = 'v2'
__version__ = '1.3' __version__ = '1.4'
APP_TYPE = 0x00 APP_TYPE = 0x00
DATA_TYPE = 0x01 DATA_TYPE = 0x01
@@ -60,6 +60,7 @@ SUBTYPES = {
BOOTLOADER_TYPE: { BOOTLOADER_TYPE: {
'primary': 0x00, 'primary': 0x00,
'ota': 0x01, 'ota': 0x01,
'recovery': 0x02,
}, },
PARTITION_TABLE_TYPE: { PARTITION_TABLE_TYPE: {
'primary': 0x00, 'primary': 0x00,
@@ -154,6 +155,7 @@ md5sum = True
secure = SECURE_NONE secure = SECURE_NONE
offset_part_table = 0 offset_part_table = 0
primary_bootloader_offset = None primary_bootloader_offset = None
recovery_bootloader_offset = None
def status(msg): def status(msg):
@@ -464,10 +466,15 @@ class PartitionDefinition(object):
return parse_int(strval) return parse_int(strval)
def parse_address(self, strval, ptype, psubtype): def parse_address(self, strval, ptype, psubtype):
if ptype == BOOTLOADER_TYPE and psubtype == SUBTYPES[ptype]['primary']: if ptype == BOOTLOADER_TYPE:
if primary_bootloader_offset is None: if psubtype == SUBTYPES[ptype]['primary']:
raise InputError(f'Primary bootloader offset is not defined. Please use --primary-bootloader-offset') if primary_bootloader_offset is None:
return primary_bootloader_offset 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']: if ptype == PARTITION_TABLE_TYPE and psubtype == SUBTYPES[ptype]['primary']:
return offset_part_table return offset_part_table
if strval == '': if strval == '':
@@ -590,6 +597,7 @@ def main():
global offset_part_table global offset_part_table
global secure global secure
global primary_bootloader_offset global primary_bootloader_offset
global recovery_bootloader_offset
parser = argparse.ArgumentParser(description='ESP32 partition table utility') parser = argparse.ArgumentParser(description='ESP32 partition table utility')
parser.add_argument('--flash-size', help='Optional flash size limit, checks partition table fits in flash', 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('--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('--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('--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('--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('--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')) 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'Unsupported configuration. Primary bootloader must be below partition table. '
f'Check --primary-bootloader-offset={primary_bootloader_offset:#x} and --offset={offset_part_table:#x}' 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: if args.extra_partition_subtypes:
add_extra_subtypes(args.extra_partition_subtypes) add_extra_subtypes(args.extra_partition_subtypes)

View File

@@ -14,7 +14,7 @@ import tempfile
import gen_esp32part as gen import gen_esp32part as gen
__version__ = '2.1' __version__ = '2.2'
COMPONENTS_PATH = os.path.expandvars(os.path.join('$IDF_PATH', 'components')) COMPONENTS_PATH = os.path.expandvars(os.path.join('$IDF_PATH', 'components'))
ESPTOOL_PY = os.path.join(COMPONENTS_PATH, 'esptool_py', 'esptool', 'esptool.py') ESPTOOL_PY = os.path.join(COMPONENTS_PATH, 'esptool_py', 'esptool', 'esptool.py')
@@ -56,13 +56,14 @@ PARTITION_BOOT_DEFAULT = _PartitionId()
class ParttoolTarget(): class ParttoolTarget():
def __init__(self, port=None, baud=None, partition_table_offset=PARTITION_TABLE_OFFSET, primary_bootloader_offset=None, partition_table_file=None, def __init__(self, port=None, baud=None, partition_table_offset=PARTITION_TABLE_OFFSET, primary_bootloader_offset=None, recovery_bootloader_offset=None,
esptool_args=[], esptool_write_args=[], esptool_read_args=[], esptool_erase_args=[]): partition_table_file=None, esptool_args=[], esptool_write_args=[], esptool_read_args=[], esptool_erase_args=[]):
self.port = port self.port = port
self.baud = baud self.baud = baud
gen.offset_part_table = partition_table_offset gen.offset_part_table = partition_table_offset
gen.primary_bootloader_offset = primary_bootloader_offset gen.primary_bootloader_offset = primary_bootloader_offset
gen.recovery_bootloader_offset = recovery_bootloader_offset
def parse_esptool_args(esptool_args): def parse_esptool_args(esptool_args):
results = list() 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('--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('--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; \ 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') overrides device attached to specified port as the partition table source when defined')
@@ -318,6 +320,9 @@ def main():
if args.primary_bootloader_offset: if args.primary_bootloader_offset:
target_args['primary_bootloader_offset'] = int(args.primary_bootloader_offset, 0) 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: if args.esptool_args:
target_args['esptool_args'] = 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()
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") set(PARTITION_TABLE_CHECK_SIZES_TOOL_PATH "${CMAKE_CURRENT_LIST_DIR}/check_sizes.py")
idf_build_get_property(build_dir BUILD_DIR) 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 ${idf_path}/components/partition_table/parttool.py -q
--partition-table-offset ${PARTITION_TABLE_OFFSET} --partition-table-offset ${PARTITION_TABLE_OFFSET}
--primary-bootloader-offset ${BOOTLOADER_OFFSET} --primary-bootloader-offset ${BOOTLOADER_OFFSET}
${RECOVERY_BOOTLOADER_OPTION}
--partition-table-file ${PARTITION_CSV_PATH} --partition-table-file ${PARTITION_CSV_PATH}
get_partition_info ${get_part_info_args} --info ${part_info} get_partition_info ${get_part_info_args} --info ${part_info}
${extra_partition_subtypes} ${extra_partition_subtypes}

View File

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