1#!/usr/bin/env python3 2# 3# Copyright (c) 2020 Nuvoton Technology Corporation 4# 5# SPDX-License-Identifier: Apache-2.0 6 7# This script will append/paste specific header to tell ROM code (Booter) of 8# NPCX EC series how to load the firmware from flash to code ram 9# Usage python3 ${ZEPHYR_BASE}/scripts/ecst.py 10# -i in_file.bin -o out_file.bin 11# [-chip <name>] [-v|-vv] 12# [-nohcrc] [-nofcrc] [-ph <offset>] 13# [-flashsize <1|2|4|8|16>] 14# [-spimaxclk <20|25|33|40|50>] 15# [-spireadmode <normal|fast|dual|quad>] 16 17import sys 18from colorama import init, Fore 19from ecst_args import EcstArgs, exit_with_failure 20from pathlib import Path 21 22# ECST Version 23ECST_VER = "2.0.1" 24 25# Offsets inside the header 26HDR_ANCHOR_OFFSET = 0 27HDR_EXTENDED_ANCHOR_OFFSET = 4 28HDR_SPI_MAX_CLK_OFFSET = 6 29HDR_SPI_READ_MODE_OFFSET = 7 30HDR_ERR_DETECTION_CONF_OFFSET = 8 31HDR_FW_LOAD_START_ADDR_OFFSET = 9 32HDR_FW_ENTRY_POINT_OFFSET = 13 33HDR_FW_ERR_DETECT_START_ADDR_OFFSET = 17 34HDR_FW_ERR_DETECT_END_ADDR_OFFSET = 21 35HDR_FW_LENGTH_OFFSET = 25 36HDR_FLASH_SIZE_OFFSET = 29 37OTP_WRITE_PROTECT_OFFSET = 30 38KEY_VALID_OFFSET = 31 39FIRMWARE_VALID_OFFSET = 32 40RESERVED_BYTES_OFFSET = 33 41HDR_FW_HEADER_SIG_OFFSET = 56 42HDR_FW_IMAGE_SIG_OFFSET = 60 43FW_IMAGE_OFFSET = 64 44 45SIGNATURE_OFFSET = 0 46POINTER_OFFSET = 4 47ARM_FW_ENTRY_POINT_OFFSET = 4 48 49# Header field known values 50FW_HDR_ANCHOR = 0x2A3B4D5E 51FW_HDR_EXT_ANCHOR_ENABLE = 0xAB1E 52FW_HDR_EXT_ANCHOR_DISABLE = 0x54E1 53FW_HDR_CRC_DISABLE = 0x00 54FW_HDR_CRC_ENABLE = 0x02 55FW_CRC_DISABLE = 0x00 56FW_CRC_ENABLE = 0x02 57HDR_PTR_SIGNATURE = 0x55AA650E 58 59BOOTLOADER_TABLE_MODE = "bt" 60 61# SPI related values 62SPI_MAX_CLOCK_20_MHZ_VAL = "20" 63SPI_MAX_CLOCK_25_MHZ_VAL = "25" 64SPI_MAX_CLOCK_33_MHZ_VAL = "33" 65SPI_MAX_CLOCK_40_MHZ_VAL = "40" 66SPI_MAX_CLOCK_50_MHZ_VAL = "50" 67 68SPI_MAX_CLOCK_20_MHZ = 0x00 69SPI_MAX_CLOCK_25_MHZ = 0x01 70SPI_MAX_CLOCK_33_MHZ = 0x02 71SPI_MAX_CLOCK_40_MHZ = 0x03 72SPI_MAX_CLOCK_50_MHZ = 0x04 73 74SPI_CLOCK_RATIO_1_VAL = 1 75SPI_CLOCK_RATIO_2_VAL = 2 76 77SPI_CLOCK_RATIO_1 = 0x00 78SPI_CLOCK_RATIO_2 = 0x08 79 80SPI_NORMAL_MODE_VAL = 'normal' 81SPI_SINGLE_MODE_VAL = 'fast' 82SPI_DUAL_MODE_VAL = 'dual' 83SPI_QUAD_MODE_VAL = 'quad' 84 85SPI_NORMAL_MODE = 0x00 86SPI_SINGLE_MODE = 0x01 87SPI_DUAL_MODE = 0x03 88SPI_QUAD_MODE = 0x04 89 90# Flash related values 91FLASH_SIZE_1_MBYTES_VAL = "1" 92FLASH_SIZE_2_MBYTES_VAL = "2" 93FLASH_SIZE_4_MBYTES_VAL = "4" 94FLASH_SIZE_8_MBYTES_VAL = "8" 95FLASH_SIZE_16_MBYTES_VAL = "16" 96 97FLASH_SIZE_1_MBYTES = 0x01 98FLASH_SIZE_2_MBYTES = 0x03 99FLASH_SIZE_4_MBYTES = 0x07 100FLASH_SIZE_8_MBYTES = 0x0f 101FLASH_SIZE_8_MBYTES = 0x0f 102FLASH_SIZE_16_MBYTES = 0x1f 103 104MAX_FLASH_SIZE = 0x03ffffff 105 106# Header fields default values. 107ADDR_16_BYTES_ALIGNED_MASK = 0x0000000f 108ADDR_4_BYTES_ALIGNED_MASK = 0x00000003 109ADDR_4K_BYTES_ALIGNED_MASK = 0x00000fff 110 111NUM_OF_BYTES = 32 112INVALID_INPUT = -1 113HEADER_SIZE = 64 114PAD_BYTE = b'\x00' 115BYTES_TO_PAD = HDR_FW_HEADER_SIG_OFFSET - RESERVED_BYTES_OFFSET 116 117# Verbose related values 118NO_VERBOSE = 0 119REG_VERBOSE = 1 120SUPER_VERBOSE = 1 121 122# Success/failure codes 123EXIT_SUCCESS_STATUS = 0 124EXIT_FAILURE_STATUS = 1 125 126def _bt_mode_handler(ecst_args): 127 """creates the bootloader table using the provided arguments. 128 129 :param ecst_args: the object representing the command line arguments. 130 """ 131 132 output_file = _set_input_and_output(ecst_args) 133 _check_chip(output_file, ecst_args) 134 135 if ecst_args.paste_firmware_header != 0: 136 _check_firmware_header_offset(output_file, ecst_args) 137 138 _copy_image(output_file, ecst_args) 139 _set_anchor(output_file, ecst_args) 140 _set_extended_anchor(output_file, ecst_args) 141 _set_spi_flash_maximum_clock(output_file, ecst_args) 142 _set_spi_flash_mode(output_file, ecst_args) 143 _set_error_detection_configuration(output_file, ecst_args) 144 _set_firmware_load_start_address(output_file, ecst_args) 145 _set_firmware_entry_point(output_file, ecst_args) 146 _set_firmware_crc_start_and_size(output_file, ecst_args) 147 _set_firmware_length(output_file, ecst_args) 148 _set_flash_size(output_file, ecst_args) 149 _set_reserved_bytes(output_file, ecst_args) 150 _set_firmware_header_crc_signature(output_file, ecst_args) 151 _set_firmware_image_crc_signature(output_file, ecst_args) 152 153 _exit_with_success() 154 155def _set_input_and_output(ecst_args): 156 """checks the input file and output and sets the output file. 157 158 checks input file existence, creates an output file according 159 to the 'output' argument. 160 161 Note: input file size has to be greater than 0, and named differently 162 from output file 163 164 :param ecst_args: the object representing the command line arguments. 165 166 :returns: output file path object, or -1 if fails 167 """ 168 input_file = ecst_args.input 169 output = ecst_args.output 170 input_file_size = 0 171 172 if not input_file: 173 exit_with_failure("Define input file, using -i flag") 174 175 input_file_path = Path(input_file) 176 177 if not input_file_path.exists(): 178 exit_with_failure(f'Cannot open {input_file}') 179 elif input_file_path.stat().st_size == 0: 180 exit_with_failure(f'BIN Input file ({input_file}) is empty') 181 else: 182 input_file_size = input_file_path.stat().st_size 183 184 if not output: 185 output_file = Path("out_" + input_file_path.name) 186 else: 187 output_file = Path(output) 188 189 if output_file.exists(): 190 if output_file.samefile(input_file_path): 191 exit_with_failure(f'Input file name {input_file} ' 192 f'should be differed from' 193 f' Output file name {output}') 194 output_file.unlink() 195 196 output_file.touch() 197 198 if ecst_args.verbose == REG_VERBOSE: 199 print(Fore.LIGHTCYAN_EX + f'\nBIN file: {input_file}, size:' 200 f' {input_file_size} bytes') 201 print(f'Output file name: {output_file.name} \n') 202 203 return output_file 204 205def _check_chip(output, ecst_args): 206 """checks if the chip entered is a legal chip, generates an error 207 and closes the application, deletes the output file if the chip name 208 is illegal. 209 210 :param output: the output file object, 211 :param ecst_args: the object representing the command line arguments. 212 """ 213 214 if ecst_args.chip_name == INVALID_INPUT: 215 message = f'Invalid chip name, ' 216 message += "should be npcx9m8, npcx9m7, npcx9m6, npcx7m7," \ 217 " npcx7m6, npcx7m5, npcx5m5 or npcx5m6." 218 _exit_with_failure_delete_file(output, message) 219 220def _set_anchor(output, ecst_args): 221 """writes the anchor value to the output file 222 223 :param output: the output file object. 224 :param ecst_args: the object representing the command line arguments. 225 """ 226 227 with output.open("r+b") as output_file: 228 output_file.seek(HDR_ANCHOR_OFFSET + ecst_args.paste_firmware_header) 229 output_file.write(FW_HDR_ANCHOR.to_bytes(4, "little")) 230 if ecst_args.verbose == REG_VERBOSE: 231 print(f'- HDR - FW Header ANCHOR - Offset ' 232 f'{HDR_ANCHOR_OFFSET} - {_hex_print_format(FW_HDR_ANCHOR)}') 233 234 output_file.close() 235 236def _set_extended_anchor(output, ecst_args): 237 """writes the extended anchor value to the output file 238 239 :param output: the output file object, 240 :param ecst_args: the object representing the command line arguments. 241 """ 242 243 with output.open("r+b") as output_file: 244 output_file.seek(HDR_EXTENDED_ANCHOR_OFFSET + \ 245 ecst_args.paste_firmware_header) 246 if ecst_args.firmware_header_crc is FW_HDR_CRC_ENABLE: 247 output_file.write(FW_HDR_EXT_ANCHOR_ENABLE.to_bytes(2, "little")) 248 anchor_to_print = _hex_print_format(FW_HDR_EXT_ANCHOR_ENABLE) 249 else: 250 output_file.write(FW_HDR_EXT_ANCHOR_DISABLE.to_bytes(2, "little")) 251 anchor_to_print = _hex_print_format(FW_HDR_EXT_ANCHOR_DISABLE) 252 253 if ecst_args.verbose == REG_VERBOSE: 254 print(f'- HDR - Header EXTENDED ANCHOR - Offset' 255 f' {HDR_EXTENDED_ANCHOR_OFFSET} - {anchor_to_print}') 256 output_file.close() 257 258 259def _check_firmware_header_offset(output, ecst_args): 260 """checks if the firmware header offset entered is valid. 261 proportions: 262 263 firmware header offset is a non-negative integer. 264 firmware header offset is 16 bytes aligned 265 firmware header offset equals/smaller than input file minus 266 FW HEADER SIZE (64 KB) 267 input file size is bigger than FW HEADER SIZE (64 KB) 268 269 :param output: the output file object, 270 :param ecst_args: the object representing the command line arguments. 271 """ 272 273 input_file = Path(ecst_args.input) 274 paste_fw_offset = ecst_args.paste_firmware_header 275 input_file_size = input_file.stat().st_size 276 277 if paste_fw_offset == INVALID_INPUT: 278 _exit_with_failure_delete_file(output, "Cannot read paste" 279 " firmware offset") 280 281 paste_fw_offset_to_print = _hex_print_format(paste_fw_offset) 282 283 if paste_fw_offset & ADDR_16_BYTES_ALIGNED_MASK != 0: 284 message = f'Paste firmware address ({paste_fw_offset_to_print}) ' \ 285 f'is not 16 bytes aligned' 286 _exit_with_failure_delete_file(output, message) 287 288 if input_file_size <= HEADER_SIZE: 289 message = f' input file size ({input_file_size} bytes) ' \ 290 f'should be bigger than fw header size ({HEADER_SIZE} bytes)' 291 _exit_with_failure_delete_file(output, message) 292 293 if input_file_size - HEADER_SIZE < paste_fw_offset: 294 message = f'FW offset ({paste_fw_offset_to_print})should be less ' \ 295 f'than input file size ({input_file_size}) minus fw header size' \ 296 f' {HEADER_SIZE}' 297 _exit_with_failure_delete_file(output, message) 298 299def _set_spi_flash_maximum_clock(output, ecst_args): 300 """Sets the maximum allowable clock frequency (for firmware loading); 301 also sets the ratio between the Core clock 302 and the SPI clock for the specified mode. 303 writes the data into the output file. 304 the application is closed, and an error is generated 305 if the fields are not valid. 306 307 Bits 2-0 - SPI MAX Clock 308 Bit 3: SPI Clock Ratio 309 310 :param output: the output file object, 311 :param ecst_args: the object representing the command line arguments. 312 """ 313 spi_max_clock_to_write = 0 314 315 with output.open("r+b") as output_file: 316 output_file.seek(HDR_SPI_MAX_CLK_OFFSET + 317 ecst_args.paste_firmware_header) 318 spi_max_clock = ecst_args.spi_flash_maximum_clock 319 spi_clock_ratio = ecst_args.spi_flash_clock_ratio 320 321 if spi_clock_ratio == SPI_CLOCK_RATIO_1_VAL: 322 spi_clock_ratio = SPI_CLOCK_RATIO_1 323 elif spi_clock_ratio == SPI_CLOCK_RATIO_2_VAL: 324 spi_clock_ratio = SPI_CLOCK_RATIO_2 325 elif spi_clock_ratio == INVALID_INPUT: 326 message = f'Cannot read SPI Clock Ratio' 327 output_file.close() 328 _exit_with_failure_delete_file(output, message) 329 else: 330 message = f'Invalid SPI Core Clock Ratio (3) - it should be 1 or 2' 331 output_file.close() 332 _exit_with_failure_delete_file(output, message) 333 334 if spi_max_clock == SPI_MAX_CLOCK_20_MHZ_VAL: 335 spi_max_clock_to_write = SPI_MAX_CLOCK_20_MHZ | spi_clock_ratio 336 output_file.write(spi_max_clock_to_write.to_bytes(1, "little")) 337 338 elif spi_max_clock == SPI_MAX_CLOCK_25_MHZ_VAL: 339 spi_max_clock_to_write = SPI_MAX_CLOCK_25_MHZ | spi_clock_ratio 340 output_file.write(spi_max_clock_to_write.to_bytes(1, "little")) 341 342 elif spi_max_clock == SPI_MAX_CLOCK_33_MHZ_VAL: 343 spi_max_clock_to_write = SPI_MAX_CLOCK_33_MHZ | spi_clock_ratio 344 output_file.write(spi_max_clock_to_write.to_bytes(1, "little")) 345 346 elif spi_max_clock == SPI_MAX_CLOCK_40_MHZ_VAL: 347 spi_max_clock_to_write = SPI_MAX_CLOCK_40_MHZ | spi_clock_ratio 348 output_file.write(spi_max_clock_to_write.to_bytes(1, "little")) 349 350 elif spi_max_clock == SPI_MAX_CLOCK_50_MHZ_VAL: 351 spi_max_clock_to_write = SPI_MAX_CLOCK_50_MHZ | spi_clock_ratio 352 output_file.write(spi_max_clock_to_write.to_bytes(1, "little")) 353 354 elif not str(spi_max_clock).isdigit(): 355 output_file.close() 356 _exit_with_failure_delete_file(output, 357 "Cannot read SPI Flash Max Clock") 358 else: 359 message = f'Invalid SPI Flash MAX Clock size ({spi_max_clock}) ' 360 message += '- it should be 20, 25, 33, 40 or 50 MHz' 361 output_file.close() 362 _exit_with_failure_delete_file(output, message) 363 364 if ecst_args.verbose == REG_VERBOSE: 365 print(f'- HDR - SPI flash MAX Clock - Offset ' 366 f'{HDR_SPI_MAX_CLK_OFFSET} - ' 367 f'{_hex_print_format(spi_max_clock_to_write)}') 368 output_file.close() 369 370def _set_spi_flash_mode(output, ecst_args): 371 """Sets the read mode used for firmware loading and enables 372 the Unlimited Burst functionality. 373 writes the data into the output file. 374 the application is closed, and an error is generated 375 if the fields are not valid. 376 377 Bits 2-0 - SPI Flash Read Mode 378 Bit 3: Unlimited Burst Mode 379 380 Note: unlimburst is not relevant for npcx5mn chips family. 381 382 :param output: the output file object, 383 :param ecst_args: the object representing the command line arguments. 384 """ 385 386 spi_flash_read_mode = ecst_args.spi_flash_read_mode 387 spi_unlimited_burst_mode = ecst_args.unlimited_burst_mode 388 spi_read_mode_to_write = 0 389 390 with output.open("r+b") as output_file: 391 output_file.seek(HDR_SPI_READ_MODE_OFFSET + 392 ecst_args.paste_firmware_header) 393 if spi_flash_read_mode == SPI_NORMAL_MODE_VAL: 394 spi_read_mode_to_write = SPI_NORMAL_MODE | spi_unlimited_burst_mode 395 output_file.write(spi_read_mode_to_write.to_bytes(1, "little")) 396 elif spi_flash_read_mode == SPI_SINGLE_MODE_VAL: 397 spi_read_mode_to_write = SPI_SINGLE_MODE | spi_unlimited_burst_mode 398 output_file.write(spi_read_mode_to_write.to_bytes(1, "little")) 399 elif spi_flash_read_mode == SPI_DUAL_MODE_VAL: 400 spi_read_mode_to_write = SPI_DUAL_MODE | spi_unlimited_burst_mode 401 output_file.write(spi_read_mode_to_write.to_bytes(1, "little")) 402 elif spi_flash_read_mode == SPI_QUAD_MODE_VAL: 403 spi_read_mode_to_write = SPI_QUAD_MODE | spi_unlimited_burst_mode 404 output_file.write(spi_read_mode_to_write.to_bytes(1, "little")) 405 else: 406 message = f'Invalid SPI Flash Read Mode ({spi_flash_read_mode}),' 407 message += 'it should be normal, fast, dual, quad' 408 output_file.close() 409 _exit_with_failure_delete_file(output, message) 410 411 if ecst_args.verbose == REG_VERBOSE: 412 print(f'- HDR - SPI flash Read Mode - Offset ' 413 f'{HDR_SPI_READ_MODE_OFFSET} - ' 414 f'{_hex_print_format(spi_read_mode_to_write)}') 415 416 output_file.close() 417 418def _set_error_detection_configuration(output, ecst_args): 419 """writes the error detection configuration value (enabled/disabled) 420 to the output file 421 422 :param output: the output file object, 423 :param ecst_args: the object representing the command line arguments. 424 """ 425 with output.open("r+b") as output_file: 426 output_file.seek(HDR_ERR_DETECTION_CONF_OFFSET + 427 ecst_args.paste_firmware_header) 428 if ecst_args.firmware_crc == FW_CRC_ENABLE: 429 err_detect_config_to_print = _hex_print_format(FW_CRC_ENABLE) 430 output_file.write(FW_CRC_ENABLE.to_bytes(1, "little")) 431 else: 432 err_detect_config_to_print = _hex_print_format(FW_CRC_DISABLE) 433 output_file.write(FW_CRC_DISABLE.to_bytes(1, "little")) 434 435 if ecst_args.verbose == REG_VERBOSE: 436 print(f'- HDR - FW CRC Enabled - Offset ' 437 f'{HDR_ERR_DETECTION_CONF_OFFSET} - ' 438 f'{err_detect_config_to_print}') 439 440 output_file.close() 441 442def _set_firmware_load_start_address(output, ecst_args): 443 """writes the fw load address to the output file 444 445 :param output: the output file object, 446 :param ecst_args: the object representing the command line arguments. 447 """ 448 start_ram = ecst_args.chip_ram_address 449 end_ram = start_ram + ecst_args.chip_ram_size 450 fw_load_addr = ecst_args.firmware_load_address 451 fw_length = ecst_args.firmware_length 452 fw_end_addr = fw_load_addr + fw_length 453 454 start_ram_to_print = _hex_print_format(start_ram) 455 end_ram_to_print = _hex_print_format(end_ram) 456 fw_load_addr_to_print = _hex_print_format(fw_load_addr) 457 fw_length_to_print = _hex_print_format(fw_length) 458 fw_end_addr_to_print = _hex_print_format(fw_end_addr) 459 460 if fw_length == INVALID_INPUT: 461 message = f'Cannot read firmware length' 462 _exit_with_failure_delete_file(output, message) 463 464 if fw_length & ADDR_16_BYTES_ALIGNED_MASK != 0: 465 message = f'Firmware length ({fw_length_to_print}) ' \ 466 f'is not 16 bytes aligned' 467 _exit_with_failure_delete_file(output, message) 468 469 if fw_load_addr is INVALID_INPUT: 470 message = f'Cannot read FW Load start address' 471 _exit_with_failure_delete_file(output, message) 472 473 if fw_load_addr & ADDR_16_BYTES_ALIGNED_MASK != 0: 474 message = f'Firmware load address ({fw_load_addr_to_print}) ' \ 475 f'is not 16 bytes aligned' 476 _exit_with_failure_delete_file(output, message) 477 478 if (fw_load_addr > end_ram) or (fw_load_addr < start_ram): 479 message = f'Firmware load address ({fw_load_addr_to_print}) ' \ 480 f'should be between start ({start_ram_to_print}) '\ 481 f'and end ({end_ram_to_print}) of RAM' 482 _exit_with_failure_delete_file(output, message) 483 484 if fw_end_addr > end_ram: 485 message = f'Firmware end address ({fw_end_addr_to_print}) should be ' 486 message += f'less than end of RAM address ({end_ram_to_print})' 487 _exit_with_failure_delete_file(output, message) 488 489 with output.open("r+b") as output_file: 490 output_file.seek(HDR_FW_LOAD_START_ADDR_OFFSET + 491 ecst_args.paste_firmware_header) 492 output_file.write(ecst_args.firmware_load_address. 493 to_bytes(4, "little")) 494 495 output_file.seek(HDR_FW_LENGTH_OFFSET + 496 ecst_args.paste_firmware_header) 497 output_file.write(fw_length.to_bytes(4, "little")) 498 499 if ecst_args.verbose == REG_VERBOSE: 500 print(f'- HDR - FW load start address - Offset ' 501 f'{HDR_FW_LOAD_START_ADDR_OFFSET} - ' 502 f'{fw_load_addr_to_print}') 503 output_file.close() 504 505def _set_firmware_entry_point(output, ecst_args): 506 """writes the fw entry point to the output file. 507 proportions: 508 509 :param output: the output file object, 510 :param ecst_args: the object representing the command line arguments. 511 """ 512 entry_pt_none = False 513 input_file_path = Path(ecst_args.input) 514 fw_entry_pt = ecst_args.firmware_entry_point 515 fw_use_arm_reset = ecst_args.use_arm_reset 516 fw_length = ecst_args.firmware_length 517 fw_load_addr = ecst_args.firmware_load_address 518 fw_end_addr = fw_load_addr + fw_length 519 520 # check if fwep flag wasn't set and set it to fw load address if needed 521 if fw_entry_pt is None: 522 entry_pt_none = True 523 fw_entry_pt = ecst_args.firmware_load_address 524 525 if not entry_pt_none and fw_use_arm_reset: 526 message = f'-usearmrst not allowed, FW entry point already set using '\ 527 f'-fwep !' 528 _exit_with_failure_delete_file(output, message) 529 530 with input_file_path.open("r+b") as input_file: 531 with output.open("r+b") as output_file: 532 if fw_use_arm_reset: 533 input_file.seek(ARM_FW_ENTRY_POINT_OFFSET + 534 ecst_args.paste_firmware_header) 535 fw_entry_byte = input_file.read(4) 536 fw_entry_pt = int.from_bytes(fw_entry_byte, "little") 537 538 if fw_entry_pt < fw_load_addr or fw_entry_pt > fw_end_addr: 539 output_file.close() 540 input_file.close() 541 message = f'Firmware entry point ' \ 542 f'({_hex_print_format(fw_entry_pt)}) ' \ 543 f'should be between the FW load address ' \ 544 f'({_hex_print_format(fw_load_addr)})' \ 545 f' and FW end address ({_hex_print_format(fw_end_addr)})' 546 547 _exit_with_failure_delete_file(output, message) 548 549 output_file.seek(HDR_FW_ENTRY_POINT_OFFSET + 550 ecst_args.paste_firmware_header) 551 output_file.write(fw_entry_pt.to_bytes(4, "little")) 552 output_file.close() 553 input_file.close() 554 555 if ecst_args.verbose == REG_VERBOSE: 556 print(f'- HDR - FW Entry point - Offset ' 557 f'{HDR_FW_ENTRY_POINT_OFFSET} - ' 558 f'{_hex_print_format(fw_entry_pt)}') 559 560def _set_firmware_crc_start_and_size(output, ecst_args): 561 """writes the fw crc start address and the crc size to the output file. 562 proportions: 563 --crc start address should be 4 byte aligned, bigger than crc end address 564 --crc size should be 4 byte aligned, and be set to firmware length minus 565 crc start offset by default 566 --crc end address is crc start address + crc size bytes 567 568 the application is closed, and an error is generated if the mentioned 569 fields not comply with the proportions. 570 571 :param output: the output file object, 572 :param ecst_args: the object representing the command line arguments. 573 """ 574 fw_crc_size = ecst_args.firmware_crc_size 575 fw_crc_start = ecst_args.firmware_crc_start 576 577 if fw_crc_size is None: 578 fw_crc_end = ecst_args.firmware_length - 1 579 # default value for crc size 580 fw_crc_size = ecst_args.firmware_length - fw_crc_start 581 ecst_args.firmware_crc_size = fw_crc_size 582 else: 583 fw_crc_end = fw_crc_start + fw_crc_size - 1 584 585 fw_crc_start_to_print = _hex_print_format(fw_crc_start) 586 fw_crc_end_to_print = _hex_print_format(fw_crc_end) 587 fw_length_to_print = _hex_print_format(ecst_args.firmware_length) 588 fw_crc_size_to_print = _hex_print_format(fw_crc_size) 589 590 if fw_crc_start & ADDR_4_BYTES_ALIGNED_MASK != 0: 591 message = f'Firmware crc offset address ' \ 592 f'({fw_crc_start_to_print}) is not 4 bytes aligned' 593 _exit_with_failure_delete_file(output, message) 594 595 if fw_crc_start > fw_crc_end: 596 message = f'CRC start address ({fw_crc_start_to_print}) should' \ 597 f' be less or equal to CRC end address ({fw_crc_end_to_print}) \n' 598 _exit_with_failure_delete_file(output, message) 599 600 if fw_crc_size & ADDR_4_BYTES_ALIGNED_MASK != 0: 601 message = f'Firmware crc size ({fw_crc_size_to_print}) ' \ 602 f'is not 4 bytes aligned' 603 _exit_with_failure_delete_file(output, message) 604 605 if fw_crc_end > ecst_args.firmware_length - 1: 606 message = f'CRC end address ({fw_crc_end_to_print}) should be less' \ 607 f' than the FW length ({fw_length_to_print}) \n' 608 _exit_with_failure_delete_file(output, message) 609 610 with output.open("r+b") as output_file: 611 output_file.seek(HDR_FW_ERR_DETECT_START_ADDR_OFFSET + 612 ecst_args.paste_firmware_header) 613 output_file.write(fw_crc_start.to_bytes(4, "little")) 614 615 output_file.seek(HDR_FW_ERR_DETECT_END_ADDR_OFFSET + 616 ecst_args.paste_firmware_header) 617 output_file.write(fw_crc_end.to_bytes(4, "little")) 618 output_file.close() 619 620 if ecst_args.verbose == REG_VERBOSE: 621 print(f'- HDR - FW CRC Start - Offset ' 622 f'{HDR_FW_ERR_DETECT_START_ADDR_OFFSET} - ' 623 f'{fw_crc_start_to_print}') 624 625 print(f'- HDR - FW CRC End - Offset ' 626 f'{HDR_FW_ERR_DETECT_END_ADDR_OFFSET} - ' 627 f'{fw_crc_end_to_print}') 628 629def _set_firmware_length(output, ecst_args): 630 """writes the flash size value to the output file 631 Note: the firmware length value has already been checked before 632 this method 633 634 :param output: the output file object, 635 :param ecst_args: the object representing the command line arguments. 636 """ 637 638 fw_length = ecst_args.firmware_length 639 fw_length_to_print = _hex_print_format(fw_length) 640 641 with output.open("r+b") as output_file: 642 output_file.seek(HDR_FW_LENGTH_OFFSET + 643 ecst_args.paste_firmware_header) 644 output_file.write(fw_length.to_bytes(4, "little")) 645 if ecst_args.verbose == REG_VERBOSE: 646 print(f'- HDR - FW Length - Offset ' 647 f'{HDR_FW_LENGTH_OFFSET} - ' 648 f'{fw_length_to_print}') 649 output_file.close() 650 651def _set_flash_size(output, ecst_args): 652 """writes the flash size value to the output file 653 valid values are 1,2,4,8 or 16 (default is 16). 654 the application is closed and the output file is deleted 655 if the flash size is invalid 656 657 :param output: the output file object, 658 :param ecst_args: the object representing the command line arguments. 659 """ 660 flash_size_to_print = "" 661 662 with output.open("r+b") as output_file: 663 output_file.seek(HDR_FLASH_SIZE_OFFSET + 664 ecst_args.paste_firmware_header) 665 flash_size = ecst_args.flash_size 666 667 if flash_size == FLASH_SIZE_1_MBYTES_VAL: 668 output_file.write(FLASH_SIZE_1_MBYTES.to_bytes(4, "little")) 669 flash_size_to_print = _hex_print_format(FLASH_SIZE_1_MBYTES) 670 elif flash_size == FLASH_SIZE_2_MBYTES_VAL: 671 output_file.write(FLASH_SIZE_2_MBYTES.to_bytes(4, "little")) 672 flash_size_to_print = _hex_print_format(FLASH_SIZE_2_MBYTES) 673 elif flash_size == FLASH_SIZE_4_MBYTES_VAL: 674 output_file.write(FLASH_SIZE_4_MBYTES.to_bytes(4, "little")) 675 flash_size_to_print = _hex_print_format(FLASH_SIZE_4_MBYTES) 676 elif flash_size == FLASH_SIZE_8_MBYTES_VAL: 677 output_file.write(FLASH_SIZE_8_MBYTES.to_bytes(4, "little")) 678 flash_size_to_print = _hex_print_format(FLASH_SIZE_8_MBYTES) 679 elif flash_size == FLASH_SIZE_16_MBYTES_VAL: 680 output_file.write(FLASH_SIZE_16_MBYTES.to_bytes(4, "little")) 681 flash_size_to_print = _hex_print_format(FLASH_SIZE_16_MBYTES) 682 elif not flash_size.isdigit(): 683 output_file.close() 684 _exit_with_failure_delete_file(output, "Cannot read Flash size") 685 else: 686 message = f'Invalid flash size ({flash_size} MBytes), ' \ 687 f' it should be 0.5, 1, 2, 4, 8, 16 MBytes \n' \ 688 f' please note - for 0.5 MBytes flash, enter \'1\' ' 689 output_file.close() 690 _exit_with_failure_delete_file(output, message) 691 692 if ecst_args.verbose == REG_VERBOSE: 693 print(f'- HDR - Flash size - Offset ' 694 f'{HDR_FLASH_SIZE_OFFSET} - ' 695 f' {flash_size_to_print}') 696 output_file.close() 697 698def _set_reserved_bytes(output, ecst_args): 699 """fills the reserved part of the image at the relevant offset 700 with the PAD_BYTE 701 702 :param output: the output file object 703 :param ecst_args: the object representing the command line arguments. 704 """ 705 with output.open("r+b") as output_file: 706 for i in range(BYTES_TO_PAD): 707 output_file.seek(RESERVED_BYTES_OFFSET + 708 ecst_args.paste_firmware_header + i) 709 output_file.write(PAD_BYTE) 710 output_file.close() 711 712def _set_firmware_header_crc_signature(output, ecst_args): 713 """writes the firmware header crc signature (4 bytes) 714 to the output file 715 716 :param output: the output file object 717 :param ecst_args: the object representing the command line arguments. 718 """ 719 crc_to_print = _hex_print_format(0) 720 721 # calculating crc only if the header crc check is enabled 722 if ecst_args.firmware_header_crc != FW_HDR_CRC_DISABLE: 723 724 with output.open("r+b") as output_file: 725 crc_calc = 0xffffffff 726 table = _create_table() 727 728 for i in range(HDR_FW_HEADER_SIG_OFFSET): 729 output_file.seek(ecst_args.paste_firmware_header + i) 730 current = output_file.read(1) 731 crc_calc = _crc_update( 732 int.from_bytes(current, "little"), crc_calc, table) 733 734 crc = _finalize_crc(crc_calc) 735 crc_to_write = crc.to_bytes(4, "little") 736 crc_to_print = _hex_print_format(crc) 737 output_file.seek(ecst_args.paste_firmware_header + 738 HDR_FW_HEADER_SIG_OFFSET) 739 output_file.write(crc_to_write) 740 741 output_file.close() 742 743 if ecst_args.verbose == REG_VERBOSE: 744 print(f'- HDR - Header CRC - Offset ' 745 f'{HDR_FW_HEADER_SIG_OFFSET} - ' 746 f' {crc_to_print}') 747 748def _set_firmware_image_crc_signature(output, ecst_args): 749 """writes the firmware image crc signature (4 bytes) 750 to the output file. 751 752 :param output: the output file object, 753 :param ecst_args: the object representing the command line arguments. 754 """ 755 # calculating crc only if the image crc check is enabled 756 crc_to_print = _hex_print_format(0) 757 if ecst_args.firmware_crc != FW_CRC_DISABLE: 758 759 with output.open("r+b") as output_file: 760 crc_calc = 0xffffffff 761 table = _create_table() 762 763 output_file.seek(FW_IMAGE_OFFSET + ecst_args.paste_firmware_header + 764 ecst_args.firmware_crc_start) 765 for _ in range(ecst_args.firmware_crc_size): 766 current = output_file.read(1) 767 crc_calc = _crc_update(int.from_bytes(current, "little"), \ 768 crc_calc, table) 769 770 crc = _finalize_crc(crc_calc) 771 crc_to_write = crc.to_bytes(4, "little") 772 crc_to_print = _hex_print_format(crc) 773 output_file.seek(HDR_FW_IMAGE_SIG_OFFSET + 774 ecst_args.paste_firmware_header) 775 output_file.write(crc_to_write) 776 777 output_file.close() 778 779 if ecst_args.verbose == REG_VERBOSE: 780 print(f'- HDR - Header CRC - Offset ' 781 f'{HDR_FW_IMAGE_SIG_OFFSET} - ' 782 f' {crc_to_print}') 783 784def _copy_image(output, ecst_args): 785 """copies the fw image from the input file to the output file 786 if firmware header offset is defined, just copies the input file to the 787 output file 788 789 :param output: the output file object, 790 :param ecst_args: the object representing the command line arguments. 791 """ 792 with open(ecst_args.input, "rb") as firmware_image: 793 with open(output, "r+b") as output_file: 794 if ecst_args.paste_firmware_header == 0: 795 output_file.seek(FW_IMAGE_OFFSET) 796 else: 797 output_file.seek(0) 798 for line in firmware_image: 799 output_file.write(line) 800 output_file.close() 801 firmware_image.close() 802 803 # pad fw image to be 16 byte aligned if needed 804 input_file_size = Path(ecst_args.input).stat().st_size 805 bytes_to_pad_num = abs((16 - input_file_size) % 16) 806 807 with open(output, "r+b") as output_file: 808 i = bytes_to_pad_num 809 while i != 0: 810 output_file.seek(0, 2) # seek end of file 811 output_file.write(PAD_BYTE) 812 i -= 1 813 output_file.close() 814 815 # update firmware length if needed 816 fw_length = ecst_args.firmware_length 817 if fw_length is None: 818 if ecst_args.paste_firmware_header == 0: 819 ecst_args.firmware_length = input_file_size + bytes_to_pad_num 820 else: 821 ecst_args.firmware_length = input_file_size - HEADER_SIZE - \ 822 ecst_args.paste_firmware_header 823 824def _merge_file_with_bt_header(ecst_args): 825 """Merge the BT with the BH according to the bhoffset and pointer flags 826 827 :param ecst_args: the object representing the command line arguments. 828 """ 829 bh_index = ecst_args.bh_offset 830 file_name_to_print = "" 831 832 if not ecst_args.input: 833 exit_with_failure('No input BIN file selected for' 834 'Bootloader header file.') 835 else: 836 input_file = Path(ecst_args.input) 837 if not input_file.exists(): 838 exit_with_failure(f'Cannot open {ecst_args.input}') 839 840 if input_file.stat().st_size < bh_index: 841 842 message = f'Bootloader header offset ({bh_index} bytes) should be ' 843 message += f'less than file size ' \ 844 f'({input_file.stat().st_size } bytes)' 845 exit_with_failure(message) 846 847 # create a file if the output parameter is not None, 848 # otherwise write to the input file 849 if ecst_args.output is not None: 850 output_file = Path(ecst_args.output) 851 output_file.touch() 852 with input_file.open("r+b") as firmware_image: 853 with output_file.open("r+b") as output_file: 854 file_name_to_print = output_file.name 855 for line in firmware_image: 856 output_file.write(line) 857 output_file.seek(bh_index) 858 output_file.write(HDR_PTR_SIGNATURE.to_bytes(4, "little")) 859 output_file.seek(bh_index + 4) 860 output_file.write(ecst_args.pointer.to_bytes(4, "little")) 861 output_file.close() 862 firmware_image.close() 863 864 else: 865 with input_file.open("r+b") as file_to_merge: 866 file_name_to_print = file_to_merge.name 867 file_to_merge.seek(bh_index + SIGNATURE_OFFSET) 868 file_to_merge.write(HDR_PTR_SIGNATURE.to_bytes(4, "little")) 869 file_to_merge.seek(bh_index + POINTER_OFFSET) 870 file_to_merge.write(ecst_args.pointer.to_bytes(4, "little")) 871 file_to_merge.close() 872 873 if ecst_args.verbose == REG_VERBOSE: 874 print(Fore.LIGHTCYAN_EX + f'BootLoader Header file:' 875 f' {file_name_to_print}') 876 print(f'Offset: {_hex_print_format(ecst_args.bh_offset)},' 877 f' Signature: {_hex_print_format(HDR_PTR_SIGNATURE)},' 878 f' Pointer: {_hex_print_format(ecst_args.pointer)}') 879 880def _create_bt_header(ecst_args): 881 """create bootloader table header, consist of 4 bytes signature and 882 4 bytes pointer 883 884 :param ecst_args: the object representing the command line arguments. 885 """ 886 if not ecst_args.output: 887 exit_with_failure("No output file selected for " 888 "Bootloader header file.") 889 else: 890 output_file = Path(ecst_args.output) 891 if not output_file.exists(): 892 output_file.touch() 893 with open(output_file, "r+b") as boot_loader_header_file: 894 boot_loader_header_file.seek(SIGNATURE_OFFSET) 895 boot_loader_header_file.write(HDR_PTR_SIGNATURE.to_bytes(4, \ 896 "little")) 897 boot_loader_header_file.seek(POINTER_OFFSET) 898 boot_loader_header_file.write(ecst_args.pointer.to_bytes(4, \ 899 "little")) 900 boot_loader_header_file.close() 901 if ecst_args.verbose == REG_VERBOSE: 902 print(Fore.LIGHTCYAN_EX + f'BootLoader Header file:' 903 f' {output_file.name}') 904 print(f'Signature: {_hex_print_format(HDR_PTR_SIGNATURE)}, ' 905 f'Pointer: {_hex_print_format(ecst_args.pointer)}') 906 907def _create_table(): 908 """helper for crc calculation""" 909 table = [] 910 for i in range(256): 911 k = i 912 for _ in range(8): 913 if k & 1: 914 k = (k >> 1) ^ 0xEDB88320 915 else: 916 k >>= 1 917 table.append(k) 918 return table 919 920def _crc_update(cur, crc, table): 921 """helper for crc calculation 922 923 :param cur 924 :param crc 925 :param table 926 """ 927 l_crc = (0x000000ff & cur) 928 929 tmp = crc ^ l_crc 930 crc = (crc >> 8) ^ table[(tmp & 0xff)] 931 return crc 932 933def _finalize_crc(crc): 934 """helper for crc calculation 935 936 :param crc 937 """ 938 final_crc = 0 939 for j in range(NUM_OF_BYTES): 940 current_bit = crc & (1 << j) 941 current_bit = current_bit >> j 942 final_crc |= current_bit << (NUM_OF_BYTES - 1) - j 943 return final_crc 944 945def _hex_print_format(value): 946 """hex representation of an integer 947 948 :param value: an integer to be represented in hex 949 """ 950 return "0x{:08x}".format(value) 951 952def _exit_with_failure_delete_file(output, message): 953 """formatted failure message printer, prints the 954 relevant error message, deletes the output file, 955 and exits the application. 956 957 :param message: the error message to be printed 958 """ 959 output_file = Path(output) 960 if output_file.exists(): 961 output_file.unlink() 962 963 message = '\n' + message 964 message += '\n' 965 message += '******************************\n' 966 message += '*** FAILED ***\n' 967 message += '******************************\n' 968 print(Fore.RED + message) 969 970 sys.exit(EXIT_FAILURE_STATUS) 971 972def _exit_with_success(): 973 """formatted success message printer, prints the 974 success message and exits the application. 975 """ 976 message = '\n' 977 message += '******************************\n' 978 message += '*** SUCCESS ***\n' 979 message += '******************************\n' 980 print(Fore.GREEN + message) 981 982 sys.exit(EXIT_SUCCESS_STATUS) 983 984def main(): 985 """main of the application 986 """ 987 init() # colored print initialization for windows 988 989 if len(sys.argv) < 2: 990 sys.exit(EXIT_FAILURE_STATUS) 991 992 ecst_obj = EcstArgs() 993 994 if ecst_obj.error_args: 995 for err_arg in ecst_obj.error_args: 996 message = f'unKnown flag: {err_arg}' 997 exit_with_failure(message) 998 sys.exit(EXIT_SUCCESS_STATUS) 999 1000 # Start to handle booter header table 1001 _bt_mode_handler(ecst_obj) 1002 1003if __name__ == '__main__': 1004 main() 1005