Dear Nordic,
I am working on OTA, we have total three controller, one is nordic(nRF5340), second is STM32 and third is Redpine(RS9116W), I am working on to do OTA of all three controller.
We are updating nordic controller by mcuboot and no issues found in that, for remain two controllers, I have created one bundle image (Contain STM32 + RS9116W firmware image along with DFU multi-image CBOR header) with the help of dfu_multi_image_tool.py script provided by nordic and add modification in nordic SDK code for download this bundle image into the nordic QSPI external flash by BLE as interface. for parsing that DFU bundle image headers we are using dfu_multi_image.c file as reference (we have added our own file by taking reference of this file to parse CBOR header data of DFU bundle image), until that we are good. we are able to parse "ID" and "size" parameter from that.
For STM and RS9116W firmware image validation we have requirement to add some meta data into DFU multi-image header (apart from ID and Size parameter, we have to add CRC and Version), for that modify the dfu_multi_image_tool.py script and create new DFU multi-image bundle.
The main problem we are facing is in CBOR header parsing in DFU multi-image bundle after adding exter meta data into it.
We have tried to add validation in "parse_image_info" function(dfu_multi_image.c) for CRC and version but it is not working for us, can anyone please help me here how we can parse CBOR header after adding extra meta data into DFU multi-image bundle.
my CBOR header is looks like below in bundle file if we took in array.
below is the script file and multi_image.c and multi_image.h file content after added modification in it.
#include <dfu/dfu_multi_image.h> #include <sys/byteorder.h> #include <sys/util.h> #include <zcbor_decode.h> #include <errno.h> #include <string.h> #include "fw_update.h" #include "multi_image.h" #define FIXED_HEADER_SIZE sizeof(uint16_t) #define CBOR_HEADER_NESTING_LEVEL 3 #define IMAGE_NO_FIXED_HEADER -2 #define IMAGE_NO_CBOR_HEADER -1 uint8_t *dfu_header; struct dfu_multi_image_ctx ctx; static bool parse_image_info(zcbor_state_t *states, struct image_info *image) { bool res; res = zcbor_map_start_decode(states); res = res && zcbor_tstr_expect_lit(states, "id"); res = res && zcbor_int32_decode(states, &image->id); res = res && zcbor_tstr_expect_lit(states, "version"); res = res && zcbor_bstr_decode(states, &image->version); res = res && zcbor_tstr_expect_lit(states, "CRC"); res = res && zcbor_uint32_decode(states, &image->crc); res = res && zcbor_tstr_expect_lit(states, "size"); res = res && zcbor_uint32_decode(states, &image->size); res = res && zcbor_map_end_decode(states); return res; } int parse_cbor_header_image(void) { bool res; uint_fast32_t image_count; ZCBOR_STATE_D(states, CBOR_HEADER_NESTING_LEVEL, ctx.buffer + FIXED_HEADER_SIZE, ctx.cur_item_size - FIXED_HEADER_SIZE, 1); res = zcbor_map_start_decode(states); res = res && zcbor_tstr_expect_lit(states, "img"); res = res && zcbor_list_start_decode(states); res = res && zcbor_multi_decode(1, CONFIG_DFU_MULTI_IMAGE_MAX_IMAGE_COUNT, &image_count, (zcbor_decoder_t *)parse_image_info, states, ctx.header.images, sizeof(struct image_info)); res = res && zcbor_list_end_decode(states); res = res && zcbor_map_end_decode(states); if (!res) { return zcbor_pop_error(states); } ctx.header.image_count = image_count; return 0; } int multi_image_init(uint8_t *buffer, size_t buffer_size) { if (buffer == NULL || buffer_size < FIXED_HEADER_SIZE) { return -EINVAL; } memset(&ctx, 0, sizeof(ctx)); ctx.buffer = buffer; ctx.buffer_size = buffer_size; ctx.cur_image_no = IMAGE_NO_FIXED_HEADER; //Kaushik added change here // ctx.cur_item_size = FIXED_HEADER_SIZE; memcpy(&ctx.cur_item_size, buffer, FIXED_HEADER_SIZE); ctx.cur_item_size += FIXED_HEADER_SIZE; return 0; }
#ifndef MULTI_IMAGE_H_ #define MULTI_IMAGE_H_ #include "dfu/dfu_multi_image.h" #include "zcbor_common.h" /****************************************************************************/ /* STRUCTURE */ /****************************************************************************/ struct image_info { int32_t id; struct zcbor_string version; // uint8_t version[14]; uint32_t crc; uint32_t size; }; struct header { struct image_info images[CONFIG_DFU_MULTI_IMAGE_MAX_IMAGE_COUNT]; size_t image_count; }; struct dfu_multi_image_ctx { /* User configuration */ uint8_t *buffer; size_t buffer_size; struct dfu_image_writer writers[CONFIG_DFU_MULTI_IMAGE_MAX_IMAGE_COUNT]; size_t writer_count; /* Parsed header */ struct header header; /* Current parser state */ int cur_image_no; size_t cur_offset; size_t cur_item_offset; size_t cur_item_size; }; int parse_cbor_header_image(void); int multi_image_init(uint8_t *buffer, size_t buffer_size); #endif /* MULTI_IMAGE_H_ */
#!/usr/bin/env python3 # # Copyright (c) 2022 Nordic Semiconductor ASA # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause """ Utility for manipulating DFU Multi Image packages. DFU Multi Image package is a general-purpose update file consisting of a CBOR-based header that describes contents of the package, followed by a number of update components, such as firmware images for different MCU cores. The CBOR header currently complies with the following format: { "img": [ {"id": 0, "size": 102400}, {"id": 1, "size": 204800} ... ] } Usage examples: Creating DFU Multi Image package: ./dfu_multi_image_tool.py create --image 0 app_update.bin --image 1 net_core_app_update.bin dfu_multi_image.bin Showing DFU Multi Image package header: ./dfu_multi_image_tool.py show dfu_multi_image.bin """ import argparse import cbor2 import struct import os # Buffer size used for file reads to ensure large files are not loaded into memory at once READ_BUFFER_SIZE = 16 * 1024 def generate_header(image: list) -> bytes: """ Generate DFU Multi Image package header """ image_data = [{'id': int(id),'version': str(version), 'CRC' : int(CRC), 'size': os.path.getsize(path)} for id, version, CRC, path in image] header_data = {'img': image_data} header_cbor = cbor2.dumps(header_data) return struct.pack('<H', len(header_cbor)) + header_cbor def parse_header(file: object) -> object: """ Parse DFU Multi Image package header """ header_fixed_size = struct.calcsize('<H') header_cbor_size, = struct.unpack('<H', file.read(header_fixed_size)) header_cbor = file.read(header_cbor_size) return cbor2.loads(header_cbor) def generate_image(images: list, output_file: str) -> None: """ Generate DFU Multi Image package """ with open(output_file, 'wb') as out_file: out_file.write(generate_header(images)) for _,_,_, path in images: with open(path, 'rb') as file: while True: chunk = file.read(READ_BUFFER_SIZE) if not chunk: break out_file.write(chunk) def show_header(input_file: str) -> None: """ Parse and print DFU Multi Image package header """ with open(input_file, 'rb') as file: header = parse_header(file) print('Images:') for image in header['img']: print(f'- Id: {image["id"]}') print(f'- Ver: {image["version"]}') print(f'- CRC: {image["CRC"]}') print(f' Size: {image["size"]}') def main(): parser = argparse.ArgumentParser(description='DFU Multi Image tool', fromfile_prefix_chars='@') subcommands = parser.add_subparsers(dest='subcommand', title='valid subcommands') create_parser = subcommands.add_parser( 'create', help='Create DFU Multi Image package') create_parser.add_argument( '-i', '-ver', '-crc', '--image', required=True, action='append', nargs=4, metavar=('id', 'version', 'CRC' ,'path'), help='Image to be included in package') create_parser.add_argument( 'output_file', help='Path to output package file') show_parser = subcommands.add_parser( 'show', help='Show DFU Multi Image package header') show_parser.add_argument( 'input_file', help='Path to package file') args = parser.parse_args() if args.subcommand == 'create': generate_image(args.image, args.output_file) elif args.subcommand == 'show': show_header(args.input_file) else: parser.print_help() if __name__ == "__main__": main()