Partition Manager¶
The Partition Manager is a Python script that sets the start address and size of all flash and RAM partitions in a multi-image build context. When creating an application that requires child images (for example, a bootloader), you can configure the Partition Manager to control where in memory each image should be placed, and how the RAM should be shared.
See Multi-image builds for more information about multi-image builds.
The Partition Manager is activated for all multi-image builds, no matter what build strategy is used for the child image.
Overview¶
The Partition Manager script reads configuration files named pm.yml
, which define flash and RAM partitions.
A flash partition’s definition includes its name and constraints on its size and placement in flash.
A RAM partition’s definition includes its name and constraints on its size.
The Partition Manager allocates a start address and sometimes a size to each partition in a way that satisfies these constraints.
There are different kinds of flash partitions and RAM partitions, as described below.
Flash partition types¶
- Image partitions
An image partition is the flash area reserved for an image, to which the image binary is written.
When the Partition Manager is active, there is one root image and one or more child images. The name of the root image is
app
; it is always implicitly defined. Child images are explicitly defined inpm.yml
files. The size of the root image partition is dynamic, while the sizes of all child image partitions are statically defined.- Placeholder partitions
A placeholder partition does not contain an image, but reserves space for other uses. For example, you might want to reserve space in flash to hold an updated application image while it is being downloaded and before it is copied to the application image partition.
- Container partitions
A container partition does not reserve space, but is used to logically and/or physically group other partitions.
The start addresses and sizes of image partitions are used in the preprocessing of the linker script for each image.
RAM partition types¶
- Default image RAM partition
The default image RAM partition consists of all RAM that is not defined as a permanent image RAM partition or placeholder RAM partition. It is the default RAM partition associated with an image and is set as the RAM region when linking the image. If an image must reserve its RAM area permanently (i.e. at the same time as other images are running), it must use a permanent image RAM partition, described below.
- Permanent image RAM partitions
A permanent image RAM partition reserves RAM for an image permanently. It is typically used for images that will remain active after they have booted the next step in the boot chain. If an image has configured a permanent image RAM partition, it is set as the RAM region when linking the image instead of the default image RAM partition.
- Permanent placeholder RAM partitions
A permanent placeholder RAM partition is used to permanently reserve RAM regions that are not associated with an image.
Configuration¶
Each child image must define its Partition Manager configuration in a file called pm.yml
.
This file must be stored in the same folder as the main CMakeLists.txt
file of the child image.
Note
pm.yml
is only used for child images.
The root application does not need to define a pm.yml
file, because its partition size and placement is implied by the size and placement of the child image partitions.
If a root application defines a pm.yml
file, it is silently ignored.
The Partition Manager configuration can be also provided by a subsystem. (Here, subsystem means a software component, like support for a file system.) Subsystem Partition Manager configurations cannot define image partitions.
See subsys/partition_manager/CMakeLists.txt
for details on how to add the subsystem-specific Partition Manager configuration files.
The code below shows how the settings
subsystem configuration is added.
if (CONFIG_SETTINGS_FCB OR CONFIG_SETTINGS_NVS)
add_partition_manager_config(pm.yml.settings)
endif()
There are some limitations when multiple application images within the same domain build the same subsystem code if it adds a Partition Manager configuration file in this way.
In particular, partition definitions are global per domain, and must be identical across calls to add_partition_manager_config()
.
If the same partition is defined twice with different configurations within a domain, the Partition Manager will fail.
Note
If Partition Manager configurations are only defined by subsystems (i.e. only one image is included in the build), the option CONFIG_PM_SINGLE_IMAGE
must be set for the Partition Manager script to be executed.
Configuration file format¶
A pm.yml
file contains partition definitions.
Each partition is defined as follows:
partition_name:
partition_property:
property_value
partition_name is the name of the partition (for example, mcuboot
).
The following partition properties and property values are available:
- placement: dict
This property specifies the placement of the partition relative to other partitions, to the start or end of flash, or to the root image
app
.A partition with the placement property set is either an image partition or a placeholder partition. If the partition name is the same as the image name (as defined in a
CMakeLists.txt
; see Defining and enabling a child image for details), this partition is the image partition. All other partitions are placeholder partitions. Eachpm.yml
file must define exactly one image partition.The placement is formatted as a YAML dict. The valid keywords are listed below:
- before: list
Place the partition before the first existing partition in the list.
- after: list
Place the partition after the first existing partition in the list.
Valid values in the lists are
app
,start
,end
, or the name of any partition. The valuestart
refers to the start address of the flash device’s memory. The valueend
refers to its end address. It is not possible to place the partition afterend
or beforestart
.- align: dict
Ensure alignment of start or end of partition by specifying a dict with a
start
orend
key respectively, where the value is the number of bytes to align to. If necessary, empty partitions are inserted in front of or behind the partition to ensure that the alignment is correct. Only one key can be specified. Partitions which directly or indirectly (through spans) share size with theapp
partitions can only be aligned if they are placed directly after theapp
partition.
- span: list OR span: string
This property is used to define container partitions. Its value may be a list or string.
Since this property is used to define container partitions, it cannot be used together with the
placement
property.If the value is a list, its elements are the names of the partitions that should be placed in the container:
# This partition spans, or contains, partition_1 through partition_n, # in any order: container_partition_name: span: [partition_1, partition_2, ..., partition_n]
The list elements are interpreted as the set of potential partitions in the container, which the Partition Manager may place in flash in any order. For example,
partition_2
could be placed beforepartition_1
.If the value is a string, it is interpreted as a list with one item:
# The following are equivalent: container_partition_name: span: foo container_partition_name: span: [foo]
Non-existent partitions are removed from the
span
list before processing, and partitions with emptyspan
lists are removed altogether (unless filled by the inside property).If the Partition Manager is forced to place a partition that is not declared in the
span
list between two partitions that are in the list, the configuration is unsatisfiable and therefore invalid. See Span property example 1 for an example of an invalid configuration.Note
You can specify configurations with an ambiguous ordering (see the following examples). Different versions of the Partition Manager script may produce different partition orders for such configurations, or fail to find a solution even if one is possible. The Partition Manager always detects unsatisfiable configurations (no false positives), but it might fail on some valid inputs (false negatives).
Here are some examples of valid and invalid configurations.
# The mcuboot and spm configurations result in this partition order: # mcuboot, spm, app mcuboot: placement: before: [spm, app] spm: placement: before: [app] # Therefore, the foo partition configuration is invalid, because spm # must be placed between mcuboot and app, but is not in the span list: foo: span: [mcuboot, app]
# These mcuboot, spm, and app configurations have two possible orders: # Order 1: mcuboot, spm, app # Order 2: mcuboot, app, spm # # In the absence of additional configuration, the Partition Manager may # choose either order. mcuboot: placement: spm: placement: after: [mcuboot] app: placement: after: [mcuboot] # However, since the following span exists, the Partition Manager should # choose order 2, since it's the only order that results in a valid # configuration for the foo partition: foo: span: [mcuboot, app]
# These mcuboot, spm, and app configurations have two possible orders: # Order 1: mcuboot, spm, app # Order 2: mcuboot, app, spm mcuboot: placement: spm: placement: after: [mcuboot] app: placement: after: [mcuboot] # However, the overall configuration is unsatisfiable: # foo requires order 2, while bar requires order 1. foo: span: [mcuboot, app] bar: span: [mcuboot, spm]
- inside: list
This property is the inverse of
span
. The name of the partition that specifies this property is added to thespan
list of the first existing container partition in the list. This property can be set for image or placeholder partitions.mcuboot: inside: [b0] b0: span: [] # During processing, this span will contain mcuboot.
- size: hexadecimal value
This property defines the size of the partition. You can provide a Kconfig option as value, which allows the user to easily modify the size (see Configuration file preprocessing for an example).
- share_size: list
This property defines the size of the current partition to be the same as the size of the first existing partition in the list. This property can be set for image or placeholder partitions. It cannot be used by container partitions. The list can contain any kind of partition.
share_size
takes precedence oversize
if one or more partitions inshare_size
exists.If the target partition is the
app
or a partition that spans over theapp
, the size is effectively split between them, because the size of theapp
is dynamically decided.If none of the partitions in the
share_size
list exists, and the partition does not define asize
property, then the partition is removed. If none of the partitions in theshare_size
list exists, and the partition does define asize
property, then thesize
property is used to set the size.- region: string
Specify the region where a partition should be placed. See Regions.
- RAM partition configuration
RAM partitions are partitions located in the
sram_primary
region. A RAM partition is specified by having the partition name end with_sram
. If a partition name consists of an image name and the ending_sram
, it is used as a permanent image RAM partition for the image.# This ... some_permament_sram_block_used_for_logging: size: 0x1000 region: sram_primary # ... is equivalent to some_permament_sram_block_used_for_logging_sram: size: 0x1000 # Specify permanent image RAM partition for MCUboot. # This will be used by the MCUboot linker script. mcuboot_sram: size: 0xa000
All occurrences of a partition name can be replaced with a dict with the key one_of
, which is resolved to the first existing partition in the one_of
value.
An error is raised if no partition inside the one_of
dict exists.
# Using 'one_of' in a list like this ... some_span: span: [something, {one_of: [does_not_exist_0, does_not_exist_1, exists1, exists2]}] # ... is equivalent to: some_span: span: [something, exists1] # Using 'one_of' as a dict value like this ... some_partition: placement: before: {one_of: [does_not_exist_0, does_not_exist_1, exists1, exists2]} # ... is equivalent to: some_partition: placement: before: exists1
Configuration file preprocessing¶
Each pm.yml
file is preprocessed to resolve symbols from Kconfig and devicetree.
The following example is taken from the pm.yml
file for the Immutable bootloader provided with the nRF Connect SDK.
It includes autoconf.h
and devicetree_legacy_unfixed.h
(generated by Kconfig and devicetree respectively) to read application configurations and hardware properties.
In this example the application configuration is used to configure the size of the image and placeholder partitions.
The application configuration is also used to decide in which region the otp
partition should be stored.
The information extracted from devicetree is the alignment value for some partitions.
#include <autoconf.h>
#include <devicetree_legacy_unfixed.h>
b0_image:
size: CONFIG_PM_PARTITION_SIZE_B0_IMAGE
placement:
after: start
b0:
span: [b0_image, provision]
s0_pad:
share_size: mcuboot_pad
placement:
after: b0
align: {start: CONFIG_FPROTECT_BLOCK_SIZE}
spm_app:
span: [spm, app]
s0_image:
# S0 spans over the image booted by B0
span: {one_of: [mcuboot, spm_app]}
s0:
# Phony container to allow hex overriding
span: [s0_pad, s0_image]
s1_pad:
# This partition will only exist if mcuboot_pad exists.
share_size: mcuboot_pad
placement:
after: s0
align: {start: DT_FLASH_ERASE_BLOCK_SIZE}
s1_image:
share_size: {one_of: [mcuboot, s0_image]}
placement:
after: [s1_pad, s0]
align: {end: CONFIG_FPROTECT_BLOCK_SIZE}
s1:
# Partition which contains the whole S1 partition.
span: [s1_pad, s1_image]
provision:
size: CONFIG_PM_PARTITION_SIZE_PROVISION
#if defined(CONFIG_SOC_NRF9160) || defined(CONFIG_SOC_NRF5340_CPUAPP)
region: otp
#else
placement:
after: b0_image
align: {start: DT_FLASH_ERASE_BLOCK_SIZE}
#endif
Regions¶
The Partition Manager places partitions in different regions. For example, you can use regions for internal flash memory and external flash memory.
To define in which region a partition should be placed, use the region
property in the configuration of the partition.
If no region is specified, the predefined internal flash region is used.
Defining a region¶
Each region is defined by a name, a start address, a size, a placement strategy, and, if applicable, a device name. A region only specifies a device name if there is a device driver associated with the region, for example, a driver for an external SPI flash.
There are three types of placement strategies, which affect how partitions are placed in regions:
- start_to_end
Place partitions sequentially from start to end. Partitions stored in a region with this placement strategy cannot affect their placement through the
placement
property. The unused part of the region is assigned to a partition with the same name as the region.- end_to_start
Place partitions sequentially from end to start. Partitions stored in a region with this placement strategy cannot affect their placement through the
placement
property. The unused part of the region is exposed through a partition with the same name as the region.- complex
Place partitions according to their
placement
configuration. The unused part of the region is exposed through a partition namedapp
.
Regions are defined in partition_manager.cmake
.
For example, see the following definitions for default regions:
add_region( # Define region without device name
otp # Name
756 # Size
0xff8108 # Base address
start_to_end # Placement strategy
)
add_region_with_dev( # Define region with device name
flash_primary # Name
${flash_size} # Size
${CONFIG_FLASH_BASE_ADDRESS} # Base address
complex # Placement strategy
NRF_FLASH_DRV_NAME # Device name
)
External flash¶
The Partition Manager supports partitions in external flash memory through the use of Regions. Any placeholder partition can specify that it should be stored in the external flash region. External flash regions always use the start_to_end placement strategy.
To use external flash, you must provide information about the device to the Partition Manager through these Kconfig options:
CONFIG_PM_EXTERNAL_FLASH
- enable external flashCONFIG_PM_EXTERNAL_FLASH_DEV_NAME
- specify the name of the flash deviceCONFIG_PM_EXTERNAL_FLASH_BASE
- specify the base addressCONFIG_PM_EXTERNAL_FLASH_SIZE
- specify the available flash size (from the base address)
The following example assumes that the flash device has been initialized as follows in the flash driver:
DEVICE_AND_API_INIT(spi_flash_memory, "name_of_flash_device", ... );
To enable external flash support in the Partition Manager, configure the following options:
# prj.conf of application
CONFIG_PM_EXTERNAL_FLASH=y
CONFIG_PM_EXTERNAL_FLASH_DEV_NAME="name_of_flash_device"
CONFIG_PM_EXTERNAL_FLASH_BASE=0x1000 # Don't touch magic stuff at the start
CONFIG_PM_EXTERNAL_FLASH_SIZE=0x7F000 # Total size of external flash from base
Now partitions can be placed in external flash:
# Name of partition
external_plz:
region: external_flash
size: CONFIG_EXTERNAL_PLZ_SIZE
Build system¶
The build system finds the child images that have been enabled and their configurations.
For each image, the Partition Manager’s CMake code infers the paths to the following files and folders from the name and from other global properties:
The
pm.yml
fileThe compiled HEX file
The generated include folder
After CMake finishes configuring the child images, the Partition Manager script is executed in configure time (execute_process
) with the lists of names and paths as argument.
The configurations generated by the Partition Manager script are imported as CMake variables (see CMake).
The Partition Manager script outputs a partitions.yml
file.
This file contains the internal state of the Partition Manager at the end of processing.
This means it contains the merged contents of all pm.yml
files, the sizes and addresses of all partitions, and other information generated by the Partition Manager.
Generated output¶
After the main Partition Manager script has finished, another script runs.
This script takes the partitions.yml
file as input and creates the following output files:
A C header file
pm_config.h
for each child image and for the root applicationA key-value file
pm.config
The header files are used in the C code, while the key-value file is imported into the CMake namespace. Both kinds of files contain, among other information, the start address and size of all partitions.
Usage¶
The output that the Partition Manager generates can be used in various areas of your code.
C code¶
When the Partition Manager is enabled, all source files are compiled with the define USE_PARTITION_MANAGER
set to 1.
If you use this define in your code, the preprocessor can choose what code to include depending on whether the Partition Manager is being used.
#if USE_PARTITION_MANAGER
#include <pm_config.h>
#define NON_SECURE_APP_ADDRESS PM_APP_ADDRESS
#else
...
HEX files¶
The Partition Manager may implicitly or explicitly assign a HEX file to a partition.
Image partitions are implicitly assigned the compiled HEX file, i.e. the HEX file that is generated when building the corresponding image. Container partitions are implicitly assigned the result of merging the HEX files that are assigned to the underlying partitions. Placeholder partitions are not implicitly assigned a HEX file.
To explicitly assign a HEX file to a partition, set the global properties partition_name_PM_HEX_FILE and partition_name_PM_TARGET in CMake, where partition_name is the name of the partition. partition_name_PM_TARGET specifies the build target that generates the HEX file specified in partition_name_PM_HEX_FILE.
See the following example, which assigns a cryptographically signed HEX file built by the sign_target
build target to the root application:
set_property(
GLOBAL PROPERTY
app_PM_HEX_FILE # Must match "*_PM_HEX_FILE"
${PROJECT_BINARY_DIR}/signed.hex
)
set_property(
GLOBAL PROPERTY
app_PM_TARGET # Must match "*_PM_TARGET"
sign_target
)
As output, the Partition Manager creates a HEX file called merged.hex
, which is programmed to the board when calling ninja flash
.
When creating merged.hex
, all assigned HEX files are included in the merge operation.
If the HEX files overlap, the conflict is resolved as follows:
HEX files assigned to container partitions overwrite HEX files assigned to their underlying partitions.
HEX files assigned to larger partitions overwrite HEX files assigned to smaller partitions.
Explicitly assigned HEX files overwrite implicitly assigned HEX files.
This means that you can overwrite a partition’s HEX file by wrapping that partition in another partition and assigning a HEX file to the new partition.
ROM report¶
When using the Partition Manager, run ninja rom_report
to see the addresses and sizes of flash partitions.
CMake¶
The CMake variables from the Partition Manager are typically used through generator expressions, because these variables are only made available late in the CMake configure stage.
To read a Partition Manager variable through a generator expression, the variable must be assigned as a target property.
The Partition Manager stores all variables as target properties on the partition_manager
target,
which means they can be used in generator expressions in the following way.
--slot-size $<TARGET_PROPERTY:partition_manager,PM_MCUBOOT_PARTITIONS_PRIMARY_SIZE>
Static configuration¶
By default, the Partition Manager dynamically places the partitions in memory. However, if you have a deployed product that consists of multiple images, where only a subset of the included images can be upgraded through a firmware update mechanism, the upgradable images must be statically configured. For example, if a device includes a non-upgradable first-stage bootloader and an upgradable application, the application image to be upgraded must be linked to the same address as the one that is deployed.
For this purpose, the Partition Manager provides static configuration to define static partitions.
The area used by the static partitions is called the static area.
The static area comes in addition to the dynamic area, which consists of the app
partition and all memory adjacent to the app
partition that is not occupied by a static partition.
Note that there is only one dynamic area.
When the Partition Manager is executed, it operates only on the dynamic area, assuming that all other memory is reserved.
Within the dynamic area, you can define new partitions or configure existing partitions even if you are using static partitions. The dynamic area is resized as required when updating the static configuration.
Configuring static partitions¶
Static partitions are defined through a YAML-formatted configuration file in the root application’s source directory.
This file is similar to the regular pm.yml
configuration files, except that it also defines the start address for all partitions.
The static configuration can be provided through a pm_static.yml
file in the application’s source directory.
Alternatively, define a PM_STATIC_YML_FILE
variable that provides the path and file name for the static configuration in the application’s CMakeLists.txt
file, as shown in the excerpt below.
# Use static partition layout to ensure consistency between builds.
# This is to ensure settings storage will be at the same location after the DFU.
set(PM_STATIC_YML_FILE
${CMAKE_CURRENT_SOURCE_DIR}/configuration/${BOARD}/pm_static_${CMAKE_BUILD_TYPE}.yml
)
The current partition configuration for a build can be found in $BUILD_DIR/partitions.yml
.
To apply the current configuration as a static configuration, copy this file to $APPLICATION_SOURCE_DIR/pm_static.yml
.
It is also possible to build a pm_static.yml
from scratch by following the description in Adding a static partition
When modifying static configurations, keep in mind the following:
There can only be one unoccupied gap per region.
All statically defined partitions in regions with
end_to_start
orstart_to_end
placement strategy must be packed at the end or start of the region, respectively.
The default flash_primary
region uses the complex
placement strategy, so these limitations do not apply there.
You can add or remove partitions as described in the following sections.
Note
If the static configuration contains an entry for the app
partition, this entry is ignored.
Adding a dynamic partition¶
New dynamic partitions that are listed in a pm.yml
file are automatically added.
However, if a partition is defined both as static partition and as dynamic partition, the dynamic definition is ignored.
Note
When resolving the relative placement of dynamic partitions, any placement properties referencing static partitions are ignored.
Adding a static partition¶
To add a static partition, add an entry for it in pm_static.yml
.
This entry must define the properties address
, size
, and - if applicable - span
.
The region defaults to flash_primary
if no region
property is specified.
partition_name:
address: 0xab00
size: 0x1000
span: [example] # Only if this partition had the span property set originally.
Note
Child images that are built with the build strategy partition_name_BUILD_STRATEGY_SKIP_BUILD or partition_name_BUILD_STRATEGY_USE_HEX_FILE must define a static partition to ensure correct placement of the dynamic partitions.
Removing a static partition¶
To remove a static partition, delete its entry in pm_static.yml
.
Only partitions adjacent to the app
partition or other removed partitions can be removed.