Skip to content

Commit bc98125

Browse files
kacperradoszewskirlubos
authored andcommitted
cmake: sysbuild: LwM2M Carrier divided DFU file generation
Adds support for outputting proprietary LwM2M Carrier DFU files using sysbuild. These are required for the divided FOTA procedure supported by the LwM2M Carrier library. Signed-off-by: Kacper Radoszewski <[email protected]>
1 parent ffbfaa4 commit bc98125

File tree

8 files changed

+197
-0
lines changed

8 files changed

+197
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#
2+
# Copyright (c) 2024 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
find_package(Python3 REQUIRED)
8+
9+
function(lwm2m_carrier_divided_dfu)
10+
sysbuild_get(${DEFAULT_IMAGE}_image_dir IMAGE ${DEFAULT_IMAGE} VAR APPLICATION_BINARY_DIR CACHE)
11+
sysbuild_get(${DEFAULT_IMAGE}_kernel_name IMAGE ${DEFAULT_IMAGE} VAR CONFIG_KERNEL_BIN_NAME KCONFIG)
12+
13+
set(app_binary "${${DEFAULT_IMAGE}_image_dir}/zephyr/${${DEFAULT_IMAGE}_kernel_name}.signed.bin")
14+
set(output_dir "${CMAKE_BINARY_DIR}/lwm2m_carrier_divided_dfu")
15+
file(MAKE_DIRECTORY ${output_dir})
16+
17+
add_custom_target(
18+
divided_dfu
19+
ALL
20+
COMMAND
21+
${PYTHON_EXECUTABLE}
22+
${ZEPHYR_NRF_MODULE_DIR}/lib/bin/lwm2m_carrier/scripts/lwm2m_carrier_divided_dfu.py
23+
--input-file ${app_binary}
24+
--version-str ${SB_CONFIG_LWM2M_CARRIER_DIVIDED_DFU_VERSION}
25+
--max-file-size ${SB_CONFIG_LWM2M_CARRIER_DIVIDED_DFU_MAX_FILE_SIZE}
26+
--output-dir ${output_dir}
27+
DEPENDS
28+
${app_binary}
29+
${DEFAULT_IMAGE}_extra_byproducts
30+
)
31+
endfunction(lwm2m_carrier_divided_dfu)
32+
33+
if(SYSBUILD)
34+
lwm2m_carrier_divided_dfu()
35+
endif()

doc/nrf/app_dev/config_and_build/output_build_files.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,13 @@ Which files you are going to use depends on the application configuration and no
101101
| | (customizable by user, sample name by default). | |
102102
| | For example: :file:`127F-0141-01020003-light_switch.zigbee`. | |
103103
+--------------------------------------+--------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------+
104+
| :file:`<version_string>_<number>.bin`| Set of application update images specific for :ref:`liblwm2m_carrier_readme`. It is required for the | Divided FOTA over LwM2M using the :ref:`liblwm2m_carrier_readme` library for |
105+
| in :file:`lwm2m_carrier_divided_dfu` | divided FOTA procedure in the SoftBank network. | single-core applications certified in the SoftBank network. |
106+
| folder | See :kconfig:option:`CONFIG_LWM2M_CARRIER_SOFTBANK_DIVIDED_FOTA` for more information. | |
107+
| | The *<version_string>* is the value of ``SB_CONFIG_LWM2M_CARRIER_DIVIDED_DFU_VERSION``. | |
108+
| | The *<number>* is a zero-padded three digit number that corresponds to the number of the file in the | |
109+
| | update sequence. For example: :file:`versionA_001.bin`. | |
110+
+--------------------------------------+--------------------------------------------------------------------------------------------------------+-------------------------------------------------------------------------------------+
104111

105112
.. _app_build_mcuboot_output:
106113

doc/nrf/libraries/bin/lwm2m_carrier/app_integration.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,13 @@ Currently, the following types of firmware upgrades are supported:
408408

409409
* MCUboot-style upgrades (:c:macro:`LWM2M_OS_DFU_IMG_TYPE_APPLICATION`)
410410
* Modem delta upgrades (:c:macro:`LWM2M_OS_DFU_IMG_TYPE_MODEM_DELTA`)
411+
* Proprietary application upgrades over multiple files (:c:macro:`LWM2M_OS_DFU_IMG_TYPE_APPLICATION_FILE`)
411412

412413
The type of upgrade is determined when the library calls the :c:func:`lwm2m_os_dfu_img_type` function in the abstraction layer upon receiving a new firmware image.
413414

414415
If MCUboot-style upgrades are enabled, the LwM2M carrier library uses the function :c:func:`lwm2m_os_dfu_application_update_validate` to validate the application image update.
415416
A ``__weak`` implementation of the function is included, which checks if the currently running image is not yet confirmed as valid (which is the case after an upgrade) and marks it appropriately.
417+
418+
The proprietary application upgrades over multiple files are currently only supported if the :kconfig:option:`CONFIG_LWM2M_CARRIER_SOFTBANK_DIVIDED_FOTA` Kconfig option is enabled.
419+
This allows the library to perform the non-standard divided FOTA procedure in the SoftBank network.
420+
The application update files required for this type of firmware upgrade can be generated during the building process by enabling the ``SB_CONFIG_LWM2M_CARRIER_DIVIDED_DFU`` sysbuild Kconfig option.

doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ Build and configuration system
6969
* ``SB_CONFIG_SECURE_APPROTECT_USER_HANDLING`` for the :kconfig:option:`CONFIG_NRF_SECURE_APPROTECT_USER_HANDLING` Kconfig option.
7070
* ``SB_CONFIG_SECURE_APPROTECT_USE_UICR`` for the :kconfig:option:`CONFIG_NRF_SECURE_APPROTECT_USE_UICR` Kconfig option.
7171

72+
* Added the ``SB_CONFIG_LWM2M_CARRIER_DIVIDED_DFU`` sysbuild Kconfig option that enables the generation of proprietary application update files required for the LwM2M carrier divided FOTA procedure.
73+
7274
* Removed the non-working support for configuring the NSIB signing key through the environmental or command line variable (``SB_SIGNING_KEY_FILE``) along with child image.
7375

7476
.. note::
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) 2024 Nordic Semiconductor ASA
4+
#
5+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
6+
7+
import argparse
8+
import os
9+
import struct
10+
import errno
11+
12+
HEADER_LENGTH = 42
13+
HEADER_MAGIC = 0x424ad2dc
14+
15+
class chunk_header:
16+
def __init__(self, number, is_last, offset, version_str):
17+
self.__number = number
18+
self.__is_last = is_last
19+
self.__offset = offset
20+
self.__version_str = version_str.encode('utf-8')
21+
self.__pack_format = '<IB?I32s'
22+
23+
def encode(self):
24+
"""
25+
Encode the header, which consists of:
26+
- Header magic (4 bytes).
27+
- File number (1 byte).
28+
- Flag indicating whether the file is the last in the sequence (1 byte).
29+
- Offset of the application update data relative to the full sequence (4 bytes).
30+
- Application version string (32 bytes).
31+
"""
32+
return struct.pack(self.__pack_format, HEADER_MAGIC, self.__number, self.__is_last, self.__offset, self.__version_str)
33+
34+
def split(image, max_chunk_size):
35+
"""
36+
Split the provided image into chunks of size of up to max_chunk_size bytes.
37+
"""
38+
chunks = []
39+
40+
with open(image, 'rb') as file:
41+
while True:
42+
chunk = file.read(max_chunk_size)
43+
if not chunk:
44+
break
45+
chunks.append(chunk)
46+
47+
return chunks
48+
49+
def parse_args():
50+
parser = argparse.ArgumentParser(
51+
description='Split the application update image into proprietary LwM2M Carrier divided DFU files',
52+
formatter_class=argparse.RawDescriptionHelpFormatter,
53+
allow_abbrev=False)
54+
55+
parser.add_argument('-i', '--input-file', required=True, help='The application update file to be divided.')
56+
parser.add_argument('-v', '--version-str', required=True, help='The application version string to be included in the header (maximum 32 bytes including the null termination).')
57+
parser.add_argument('-s', '--max-file-size', default=100, type=int, help='(Optional) The maximum size of a divided DFU file in KB.')
58+
parser.add_argument('-o', '--output-dir', required=True, help='The directory to store the divided DFU files into.')
59+
60+
return parser.parse_args()
61+
62+
def main():
63+
# Parse arguments.
64+
args = parse_args()
65+
66+
# Validate the length of the version string.
67+
if len(args.version_str) > 31:
68+
raise OSError(errno.EINVAL, "Input version string is too long")
69+
70+
input_file = args.input_file
71+
version_str = args.version_str
72+
max_chunk_size = (args.max_file_size * 1024) - HEADER_LENGTH
73+
output_dir = args.output_dir
74+
75+
# Split the application update image into chunks of max_chunk_size bytes, which excludes
76+
# the length of the header from the maximum size of a divided DFU file.
77+
chunks = split(input_file, max_chunk_size)
78+
total_chunks = len(chunks)
79+
80+
# Variable to track the offset of each chunk within the whole image.
81+
offset = 0
82+
83+
print("Generating LwM2M Carrier divided DFU files...")
84+
85+
# Append headers to each chunk and store in the output directory.
86+
for i, chunk in enumerate(chunks):
87+
# Create the chunk header (1-indexed numbering).
88+
header = chunk_header(i + 1, (i == total_chunks - 1), offset, version_str)
89+
90+
# Increase the offset (image chunk only, no header included).
91+
offset += len(chunk)
92+
93+
# Append the header to the chunk.
94+
chunk = header.encode() + chunk
95+
96+
# Generate file name of each chunk.
97+
chunk_version_str = version_str + '_{0:03d}.bin'.format(i + 1)
98+
99+
# Write the chunk into the output directory.
100+
with open(os.path.join(output_dir, chunk_version_str), 'wb') as chunk_file:
101+
chunk_file.write(chunk)
102+
chunk_file.close()
103+
print("Encoded %6d bytes to %s" % (len(chunk), chunk_version_str))
104+
105+
print("LwM2M Carrier divided DFU files generated")
106+
107+
if __name__ == '__main__':
108+
try:
109+
main()
110+
except Exception:
111+
print("Failed to generate LwM2M Carrier divided DFU files")
112+
exit(1)

sysbuild/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,10 @@ function(${SYSBUILD_CURRENT_MODULE_NAME}_post_cmake)
621621
include(${ZEPHYR_NRF_MODULE_DIR}/cmake/mesh_dfu_metadata.cmake)
622622
endif()
623623

624+
if(SB_CONFIG_LWM2M_CARRIER_DIVIDED_DFU)
625+
include(${ZEPHYR_NRF_MODULE_DIR}/cmake/sysbuild/lwm2m_carrier_divided_dfu.cmake)
626+
endif()
627+
624628
include(${ZEPHYR_NRF_MODULE_DIR}/cmake/extensions.cmake)
625629
if(SB_CONFIG_PARTITION_MANAGER)
626630
# Run partition manager for each image before running CMake.

sysbuild/Kconfig.lwm2m_carrier

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Copyright (c) 2024 Nordic Semiconductor
2+
#
3+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
4+
5+
menuconfig LWM2M_CARRIER_DIVIDED_DFU
6+
bool "Proprietary LwM2M Carrier divided DFU file generation"
7+
depends on SOC_SERIES_NRF91X
8+
depends on BOOTLOADER_MCUBOOT
9+
depends on MCUBOOT_MODE_SWAP_WITHOUT_SCRATCH || MCUBOOT_MODE_SWAP_SCRATCH
10+
depends on MCUBOOT_UPDATEABLE_IMAGES = "1"
11+
help
12+
Generate proprietary application update files required for the LwM2M carrier divided
13+
FOTA procedure.
14+
15+
if LWM2M_CARRIER_DIVIDED_DFU
16+
17+
config LWM2M_CARRIER_DIVIDED_DFU_MAX_FILE_SIZE
18+
int "Divided DFU maximum file size"
19+
range 1 256
20+
default 100
21+
help
22+
Maximum size of the divided DFU files (in kilobytes) that will be generated.
23+
24+
config LWM2M_CARRIER_DIVIDED_DFU_VERSION
25+
string "Divided DFU version string"
26+
default "version"
27+
help
28+
Version to be included in the header of the divided DFU files.
29+
The string must not be longer than 32 bytes (including the null termination).
30+
31+
endif # LWM2M_CARRIER_DIVIDED_DFU

sysbuild/Kconfig.sysbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,3 +81,4 @@ rsource "Kconfig.wifi"
8181
rsource "Kconfig.suit"
8282
rsource "Kconfig.sdp"
8383
rsource "Kconfig.approtect"
84+
rsource "Kconfig.lwm2m_carrier"

0 commit comments

Comments
 (0)