Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of example-Ethernet-mbed-Cloud-connect by
combine_bootloader_with_app.py
00001 #!/usr/bin/env python 00002 00003 ## ---------------------------------------------------------------------------- 00004 ## Copyright 2016-2017 ARM Ltd. 00005 ## 00006 ## SPDX-License-Identifier: Apache-2.0 00007 ## 00008 ## Licensed under the Apache License, Version 2.0 (the "License"); 00009 ## you may not use this file except in compliance with the License. 00010 ## You may obtain a copy of the License at 00011 ## 00012 ## http://www.apache.org/licenses/LICENSE-2.0 00013 ## 00014 ## Unless required by applicable law or agreed to in writing, software 00015 ## distributed under the License is distributed on an "AS IS" BASIS, 00016 ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00017 ## See the License for the specific language governing permissions and 00018 ## limitations under the License. 00019 ## ---------------------------------------------------------------------------- 00020 00021 from os import path 00022 import json 00023 import hashlib, zlib, struct 00024 import time 00025 import sys 00026 from intelhex import IntelHex 00027 00028 ''' 00029 define FIRMWARE_HEADER_MAGIC 0x5a51b3d4UL 00030 define FIRMWARE_HEADER_VERSION 2 00031 define ARM_UC_SHA512_SIZE (512/8) 00032 define ARM_UC_GUID_SIZE (128/8) 00033 typedef struct _arm_uc_internal_header_t 00034 { 00035 /* Metadata-header specific magic code */ 00036 uint32_t headerMagic; 00037 00038 /* Revision number for metadata header. */ 00039 uint32_t headerVersion; 00040 00041 /* Version number accompanying the firmware. Larger numbers imply more 00042 recent and preferred versions. This is used for determining the 00043 selection order when multiple versions are available. For downloaded 00044 firmware the manifest timestamp is used as the firmware version. 00045 */ 00046 uint64_t firmwareVersion; 00047 00048 /* Total space (in bytes) occupied by the firmware BLOB. */ 00049 uint64_t firmwareSize; 00050 00051 /* Firmware hash calculated over the firmware size. Should match the hash 00052 generated by standard command line tools, e.g., shasum on Linux/Mac. 00053 */ 00054 uint8_t firmwareHash[ARM_UC_SHA512_SIZE]; 00055 00056 /* The ID for the update campaign that resulted in the firmware update. 00057 */ 00058 uint8_t campaign[ARM_UC_GUID_SIZE]; 00059 00060 /* Size of the firmware signature. Must be 0 if no signature is supplied. */ 00061 uint32_t firmwareSignatureSize; 00062 00063 /* Header 32 bit CRC. Calculated over the entire header, including the CRC 00064 field, but with the CRC set to zero. 00065 */ 00066 uint32_t headerCRC; 00067 00068 /* Optional firmware signature. Hashing algorithm should be the same as the 00069 one used for the firmware hash. The firmwareSignatureSize must be set. 00070 */ 00071 uint8_t firmwareSignature[0]; 00072 } arm_uc_internal_header_t; 00073 ''' 00074 00075 # define defaults to go into the metadata header 00076 SIZEOF_SHA512 = int(512/8) 00077 SIZEOF_GUID = int(128/8) 00078 FIRMWARE_HEADER_MAGIC = 0x5a51b3d4 00079 FIRMWARE_HEADER_VERSION = 2 00080 header_format = ">2I2Q{}s{}s2I".format(SIZEOF_SHA512, SIZEOF_GUID) 00081 00082 if sys.version_info < (3,): 00083 def b(x): 00084 return bytearray(x) 00085 else: 00086 def b(x): 00087 return x 00088 00089 def create_header(app_blob, firmwareVersion): 00090 # calculate the hash of the application 00091 firmwareHash = hashlib.sha256(app_blob).digest() 00092 00093 # calculate the total size which is defined as the application size + metadata header 00094 firmwareSize = len(app_blob) 00095 00096 # set campaign GUID to 0 00097 campaign = b'\00' 00098 00099 # signature not supported, set size to 0 00100 signatureSize = 0 00101 00102 print ('imageSize: {}'.format(firmwareSize)) 00103 print ('imageHash: {}'.format(''.join(['{:0>2x}'.format(c) for c in b(firmwareHash)]))) 00104 print ('imageversion: {}'.format(firmwareVersion)) 00105 00106 # construct struct for CRC calculation 00107 headerCRC = 0 00108 FirmwareHeader = struct.pack(header_format, 00109 FIRMWARE_HEADER_MAGIC, 00110 FIRMWARE_HEADER_VERSION, 00111 firmwareVersion, 00112 firmwareSize, 00113 firmwareHash, 00114 campaign, 00115 signatureSize, 00116 headerCRC) 00117 00118 # calculate checksum over header, including signatureSize but without headerCRC 00119 headerCRC = zlib.crc32(FirmwareHeader[:-4]) & 0xffffffff 00120 00121 # Pack the data into a binary blob 00122 FirmwareHeader = struct.pack(header_format, 00123 FIRMWARE_HEADER_MAGIC, 00124 FIRMWARE_HEADER_VERSION, 00125 firmwareVersion, 00126 firmwareSize, 00127 firmwareHash, 00128 campaign, 00129 signatureSize, 00130 headerCRC) 00131 00132 return FirmwareHeader 00133 00134 00135 def combine(bootloader_fn, app_fn, app_addr, hdr_addr, bootloader_addr, output_fn, version, no_bootloader): 00136 ih = IntelHex() 00137 00138 bootloader_format = bootloader_fn.split('.')[-1] 00139 00140 # write the bootloader 00141 if not no_bootloader: 00142 print("Using bootloader %s" % bootloader_fn) 00143 if bootloader_format == 'hex': 00144 print("Loading bootloader from hex file.") 00145 ih.fromfile(bootloader_fn, format=bootloader_format) 00146 elif bootloader_format == 'bin': 00147 print("Loading bootloader to address 0x%08x." % bootloader_addr) 00148 ih.loadbin(bootloader_fn, offset=bootloader_addr) 00149 else: 00150 print('Bootloader format can only be .bin or .hex') 00151 exit(-1) 00152 00153 # write firmware header 00154 app_format=app_fn.split('.')[-1] 00155 if app_format == 'bin': 00156 with open(app_fn, 'rb') as fd: 00157 app_blob = fd.read() 00158 elif app_format == 'hex': 00159 application = IntelHex(app_fn) 00160 app_blob = application.tobinstr() 00161 FirmwareHeader = create_header(app_blob, version) 00162 print("Writing header to address 0x%08x." % hdr_addr) 00163 ih.puts(hdr_addr, FirmwareHeader) 00164 00165 # write the application 00166 if app_format == 'bin': 00167 print("Loading application to address 0x%08x." % app_addr) 00168 ih.loadbin(app_fn, offset=app_addr) 00169 elif app_format == 'hex': 00170 print("Loading application from hex file") 00171 ih.fromfile(app_fn, format=app_format) 00172 00173 # output to file 00174 ih.tofile(output_fn, format=output_fn.split('.')[-1]) 00175 00176 00177 if __name__ == '__main__': 00178 from glob import glob 00179 import argparse 00180 00181 parser = argparse.ArgumentParser( 00182 description='Combine bootloader with application adding metadata header.') 00183 00184 def addr_arg(s): 00185 if not isinstance(s, int): 00186 s = eval(s) 00187 00188 return s 00189 00190 bin_map = { 00191 'k64f': { 00192 'mem_start': '0x0' 00193 }, 00194 'ublox_evk_odin_w2': { 00195 'mem_start': '0x08000000' 00196 }, 00197 'nucleo_f429zi': { 00198 'mem_start': '0x08000000' 00199 } 00200 } 00201 00202 curdir = path.dirname(path.abspath(__file__)) 00203 00204 def parse_mbed_app_addr(mcu, key): 00205 mem_start = bin_map[mcu]["mem_start"] 00206 with open(path.join(curdir, "..", "mbed_app.json")) as fd: 00207 mbed_json = json.load(fd) 00208 addr = mbed_json["target_overrides"][mcu.upper()][key] 00209 return addr_arg(addr) 00210 00211 # specify arguments 00212 parser.add_argument('-m', '--mcu', type=lambda s : s.lower().replace("-","_"), required=False, 00213 help='mcu', choices=bin_map.keys()) 00214 parser.add_argument('-b', '--bootloader', type=argparse.FileType('rb'), required=False, 00215 help='path to the bootloader binary') 00216 parser.add_argument('-a', '--app', type=argparse.FileType('rb'), required=True, 00217 help='path to application binary') 00218 parser.add_argument('-c', '--app-addr', type=addr_arg, required=False, 00219 help='address of the application') 00220 parser.add_argument('-d', '--header-addr', type=addr_arg, required=False, 00221 help='address of the firmware metadata header') 00222 parser.add_argument('-o', '--output', type=argparse.FileType('wb'), required=True, 00223 help='output combined file path') 00224 parser.add_argument('-s', '--set-version', type=int, required=False, 00225 help='set version number', default=int(time.time())) 00226 parser.add_argument('-nb', '--no-bootloader',action='store_true', required=False, 00227 help='Produce output without bootloader. The output only '+ 00228 'contains header + app. requires hex output format') 00229 00230 # workaround for http://bugs.python.org/issue9694 00231 parser._optionals.title = "arguments" 00232 00233 # get and validate arguments 00234 args = parser.parse_args() 00235 00236 # validate the output format 00237 f = args.output.name.split('.')[-1] 00238 if f == 'hex': 00239 output_format = 'hex' 00240 elif f == 'bin': 00241 output_format = 'bin' 00242 else: 00243 print('Output format can only be .bin or .hex') 00244 exit(-1) 00245 00246 # validate no-bootloader option 00247 if args.no_bootloader and output_format == 'bin': 00248 print('--no-bootloader option requires the output format to be .hex') 00249 exit(-1) 00250 00251 # validate that we can find a bootloader or no_bootloader is specified 00252 bootloader = None 00253 if not args.no_bootloader: 00254 if args.mcu and not args.bootloader: 00255 bl_list = glob("tools/mbed-bootloader-{}-*".format(args.mcu)) 00256 if len(bl_list) == 0: 00257 print("Specified MCU does not have a binary in this location " + \ 00258 "Please specify bootloader location with -b") 00259 exit(-1) 00260 elif len(bl_list) > 1: 00261 print("Specified MCU have more than one binary in this location " + \ 00262 "Please specify bootloader location with -b") 00263 print(bl_list) 00264 exit(-1) 00265 else: 00266 fname = bl_list[0] 00267 bootloader = open(fname, 'rb') 00268 elif args.bootloader: 00269 bootloader = args.bootloader 00270 elif not (args.mcu or args.bootloader): 00271 print("Please specify bootloader location -b or MCU -m") 00272 exit(-1) 00273 00274 # get the path of bootloader, application and output 00275 if bootloader: 00276 bootloader_fn = path.abspath(bootloader.name) 00277 bootloader.close() 00278 else: 00279 bootloader_fn = '' 00280 00281 if bootloader_fn.split('.')[-1] != 'hex' and not args.mcu: 00282 print("Please provide a bootloader in hex format or specify MCU -m") 00283 exit(-1) 00284 00285 app_fn = path.abspath(args.app.name) 00286 args.app.close() 00287 output_fn = path.abspath(args.output.name) 00288 args.output.close() 00289 00290 # Use specified addresses or default if none are provided 00291 app_format = app_fn.split('.')[-1] 00292 if(not (args.mcu or args.app_addr or app_format == 'hex')): 00293 print("Please specify app address or MCU") 00294 exit(-1) 00295 if app_format != 'hex': 00296 app_addr = args.app_addr or parse_mbed_app_addr(args.mcu, "target.mbed_app_start") 00297 else: 00298 app_addr = None 00299 00300 if args.mcu: 00301 mem_start = addr_arg(bin_map[args.mcu]["mem_start"]) 00302 else: 00303 mem_start = 0 00304 00305 if(not (args.mcu or args.header_addr)): 00306 print("Please specify header address or MCU") 00307 exit(-1) 00308 header_addr = args.header_addr or parse_mbed_app_addr(args.mcu, "update-client.application-details") 00309 00310 # combine application and bootloader adding metadata info 00311 combine(bootloader_fn, app_fn, app_addr, header_addr, mem_start, 00312 output_fn, args.set_version, args.no_bootloader) 00313 00314 # print the output file path 00315 print('Combined binary:' + output_fn)
Generated on Wed Jul 13 2022 01:52:44 by
1.7.2
