def get_file_buf(self, filename): fil = open(filename, 'r') hexfile = IntelHex(fil) glog.info('Min addr >> ', hexfile.minaddr()) assert hexfile.minaddr() == 0 return hexfile.tobinstr()
def __init__(self, port, speed): port = port speed = speed self.hex = IntelHex() self.port = port self.speed = speed
#see if we are preserving the mac mac = None if args.savemac: mac = jlink.make_mac_bin(None, device) else: mac = jlink.make_mac_bin(args.mac, None) if args.app: appStr = args.app #get size of app and create binary file filename, extension = os.path.splitext(args.app) appSize = 0 if extension == '.hex': utils.deleteIfExists('temp.bin', None) ih = IntelHex(args.app) ih.tobinfile('temp.bin') appSize = os.path.getsize('temp.bin') else: appSize = os.path.getsize(args.app) jlink.make_bl_settings_bin(appSize) print(appSize) utils.deleteIfExists('temp.bin', None) else: jlink.make_bl_settings_bin(0) #validate existence of all necessary binary files; application validated earlier in script if cfg.softdevice.compatible == 'True': if not os.path.exists(softdeviceStr): if args.softdevice != 'none':
def __init__(self, filename): """Opens a file by mmmap() for use within GoodHex.""" from intelhex import IntelHex self.ihex = IntelHex(filename) self.ihex.padding = None
def save(self, path): h = IntelHex() h.frombytes(bytes=self.payload, offset=self.base_addr) h.tofile(path, 'hex')
def __init__(self, out): self.hex = IntelHex() self.out = out
def brp_otp(): global brp_otp_state brp_otp_state = SHA256.new(brp_otp_state).digest() return brp_otp_state for i in range(0, brp_seed_iterations): brp_otp() BRP_APW_odd = brp_otp() BRP_APW = bytearray() for b in BRP_APW_odd: BRP_APW.append(0xF8) BRP_APW.append(b) brp_otp_state = SHA256.new(BRP_APW).digest() ih = IntelHex() ih.loadhex(ihexf) all_sections = ih.segments() print("input hex file sections:") for sec in all_sections: print("0x%08X 0x%08X" % (sec[0], sec[1] - 1)) #ensure we have a single section assert (1 == len(all_sections)) #get BRP as raw bytes (boot_rom_patch shall be position independent code) BRP = bytearray() for sec in all_sections: for i in range(sec[0], sec[1]): BRP.append(ih[i])
def tfm_sign_image(tfm_import_path, signing_key, signing_key_1, non_secure_bin): SECURE_ROOT = abspath(tfm_import_path) secure_bin = path_join(SECURE_ROOT, 'tfm_s.bin') assert os.path.isfile(secure_bin) non_secure_bin = abspath(non_secure_bin) assert os.path.isfile(non_secure_bin) build_dir = dirname(non_secure_bin) tempdir = path_join(build_dir, 'temp') if not isdir(tempdir): os.makedirs(tempdir) flash_layout = path_join(SECURE_ROOT, 'partition', 'flash_layout.h') bl2_bin = path_join(SECURE_ROOT, 'bl2.bin') s_bin_basename = splitext(basename(secure_bin))[0] ns_bin_basename = splitext(basename(non_secure_bin))[0] signing_key = path_join(SECURE_ROOT, 'signing_key', signing_key) assert os.path.isfile(signing_key) # Find Python 3 command name across platforms python3_cmd = "python3" if shutil.which("python3") is not None else "python" # Specify image version # # MCUboot image version format: Major.Minor.Revision+Build # # Requirements for image version: # 1. Major.Minor.Revision must be non-decremental when used to derive security # counter (-s 'auto'). # 2. Make Major.Minor.Revision+Build incremental to identify the firmware # itself uniquely through psa_fwu_query(). # 3. Get around MCUboot failure with: # [INF] Starting bootloader # [INF] Swap type: none # [ERR] Failed to add Image 0 data to shared memory area # [ERR] Unable to find bootable image # This is because TF-M underestimates MAX_BOOT_RECORD_SZ for boot record # where Major.Minor.Revision will pack into during signing. The more digits # of the Major.Minor.Revision, the larger the needed boot record size. And # then MCUboot errors in boot_save_boot_status(). # # To meet all the above requirements, we apply the following policy: # 1. To not change MAX_BOOT_RECORD_SZ in TF-M, specify Major.Minor.Revision # with TF-M version instead of modified Unix timestamp. This needs less digits to # fit into MAX_BOOT_RECORD_SZ. # 2. To make Major.Minor.Revision+Build incremental, specify the Build part with # modified Unix timestamp. # 3. To make security counter non-decremental, we can derive it from # Major.Minor.Revision (-s 'auto') or explicitly specify it with modified # Unix timestamp, depending on security consideration. # # NOTE: To get around Y2038 problem, we modify Unix timestamp by setting new base # point. Using 32-bit unsigned integer to hold the modified Unix timestamp, # it will break (wrap around) after Y2156 (2106 + 2020 - 1970). # https://en.wikipedia.org/wiki/Year_2038_problem # modified_timestamp = int(datetime.now().timestamp()) - int(datetime(2020, 1, 1).timestamp()) img_ver_major = 1 # Instead of (modified_timestamp >> 24) & 0xFF img_ver_minor = 4 # Instead of (modified_timestamp >> 16) & 0xFF img_ver_revision = 0 # Instead of modified_timestamp & 0xFFFF img_ver_build = modified_timestamp # wrapper.py command template cmd_wrapper = [ python3_cmd, path_join(MBED_OS_ROOT, "tools", "psa", "tfm", "bin_utils", "wrapper.py"), "-v", "{}.{}.{}+{}".format(img_ver_major, img_ver_minor, img_ver_revision, img_ver_build), "-k", "SIGNING_KEY_PATH", "--layout", "IMAGE_MACRO_PATH", "--public-key-format", 'full', "--align", '1', # Reasons for removing padding and boot magic option "--pad": # 1. PSA FWU API psa_fwu_install() will be responsible for writing boot magic to enable upgradeable. # 2. The image size gets smaller instead of slot size. #"--pad", "--pad-header", "-H", '0x400', "--overwrite-only", "-s", 'auto', # Or modified_timestamp "-d", '(IMAGE_ID,MAJOR.MINOR.REVISION+BUILD)', "RAW_BIN_PATH", "SIGNED_BIN_PATH", ] pos_wrapper_signing_key = cmd_wrapper.index("-k") + 1 pos_wrapper_layout = cmd_wrapper.index("--layout") + 1 pos_wrapper_dependency = cmd_wrapper.index("-d") + 1 pos_wrapper_raw_bin = len(cmd_wrapper) - 2 pos_wrapper_signed_bin = len(cmd_wrapper) - 1 # assemble.py command template cmd_assemble = [ python3_cmd, path_join(MBED_OS_ROOT, "tools", "psa", "tfm", "bin_utils", "assemble.py"), "--layout", "IMAGE_MACRO_PATH", "-s", "SECURE_BIN_PATH", "-n", "NONSECURE_BIN_PATH", "-o", "CONCATENATED_BIN_PATH", ] pos_assemble_layout = cmd_assemble.index("--layout") + 1 pos_assemble_secure_bin = cmd_assemble.index("-s") + 1 pos_assemble_nonsecure_bin = cmd_assemble.index("-n") + 1 pos_assemble_concat_bin = cmd_assemble.index("-o") + 1 # If second signing key is passed down, go signing separately; otherwise, go signing together. if signing_key_1 is not None: signing_key_1 = path_join(SECURE_ROOT, 'signing_key', signing_key_1) assert os.path.isfile(signing_key_1) image_macros_s = path_join(SECURE_ROOT, 'partition', 'signing_layout_s_preprocessed.h') image_macros_ns = path_join(SECURE_ROOT, 'partition', 'signing_layout_ns_preprocessed.h') assert os.path.isfile(image_macros_s) assert os.path.isfile(image_macros_ns) s_signed_bin = abspath(path_join(tempdir, 'tfm_s_signed' + '.bin')) ns_signed_bin = abspath(path_join(tempdir, 'tfm_' + ns_bin_basename + '_signed' + '.bin')) signed_concat_bin = abspath(path_join(tempdir, 'tfm_s_signed_' + ns_bin_basename + '_signed_concat' + '.bin')) s_update_bin = abspath(path_join(build_dir, s_bin_basename + '_update' + '.bin')) ns_update_bin = abspath(path_join(build_dir, ns_bin_basename + '_update' + '.bin')) #1. Run wrapper to sign the secure TF-M binary cmd_wrapper[pos_wrapper_signing_key] = signing_key cmd_wrapper[pos_wrapper_layout] = image_macros_s cmd_wrapper[pos_wrapper_dependency] = '(1,0.0.0+0)' # Minimum version of non-secure image required for upgrading to the secure image cmd_wrapper[pos_wrapper_raw_bin] = secure_bin cmd_wrapper[pos_wrapper_signed_bin] = s_signed_bin retcode = run_cmd(cmd_wrapper, MBED_OS_ROOT) if retcode: raise Exception("Unable to sign " + "TF-M Secure" + " binary, Error code: " + str(retcode)) return #2. Run wrapper to sign the non-secure mbed binary cmd_wrapper[pos_wrapper_signing_key] = signing_key_1 cmd_wrapper[pos_wrapper_layout] = image_macros_ns cmd_wrapper[pos_wrapper_dependency] = '(0,0.0.0+0)' # Minimum version of secure image required for upgrading to the non-secure image cmd_wrapper[pos_wrapper_raw_bin] = non_secure_bin cmd_wrapper[pos_wrapper_signed_bin] = ns_signed_bin retcode = run_cmd(cmd_wrapper, MBED_OS_ROOT) if retcode: raise Exception("Unable to sign " + "TF-M Secure" + " binary, Error code: " + str(retcode)) return #3. Concatenate signed secure TF-M binary and signed non-secure mbed binary cmd_assemble[pos_assemble_layout] = image_macros_s cmd_assemble[pos_assemble_secure_bin] = s_signed_bin cmd_assemble[pos_assemble_nonsecure_bin] = ns_signed_bin cmd_assemble[pos_assemble_concat_bin] = signed_concat_bin retcode = run_cmd(cmd_assemble, MBED_OS_ROOT) if retcode: raise Exception("Unable to concatenate " + "Secure TF-M (signed)/Non-secure Mbed (signed)" + " binaries, Error code: " + str(retcode)) return #4. Concatenate MCUboot and concatenated signed secure TF-M binary/signed non-secure mbed binary flash_area_0_offset = find_flash_area_0_offset(flash_layout) out_ih = IntelHex() out_ih.loadbin(bl2_bin) out_ih.loadbin(signed_concat_bin, flash_area_0_offset) out_ih.tofile(splitext(non_secure_bin)[0] + ".hex", 'hex') out_ih.tobinfile(non_secure_bin) # Generate firmware update file for PSA Firmware Update shutil.copy(s_signed_bin, s_update_bin) shutil.copy(ns_signed_bin, ns_update_bin) else: image_macros_s_ns = path_join(SECURE_ROOT, 'partition', 'signing_layout_preprocessed.h') assert os.path.isfile(image_macros_s_ns) concat_bin = abspath(path_join(tempdir, 'tfm_s_' + ns_bin_basename + ".bin")) concat_signed_bin = abspath(path_join(tempdir, 'tfm_s_' + ns_bin_basename + '_signed' + ".bin")) update_bin = abspath(path_join(build_dir, ns_bin_basename + '_update' + '.bin')) #1. Concatenate secure TFM and non-secure mbed binaries cmd_assemble[pos_assemble_layout] = image_macros_s_ns cmd_assemble[pos_assemble_secure_bin] = secure_bin cmd_assemble[pos_assemble_nonsecure_bin] = non_secure_bin cmd_assemble[pos_assemble_concat_bin] = concat_bin retcode = run_cmd(cmd_assemble, MBED_OS_ROOT) if retcode: raise Exception("Unable to concatenate " + "Secure TF-M/Non-secure Mbed" + " binaries, Error code: " + str(retcode)) return #2. Run wrapper to sign the concatenated binary cmd_wrapper[pos_wrapper_signing_key] = signing_key cmd_wrapper[pos_wrapper_layout] = image_macros_s_ns cmd_wrapper[pos_wrapper_dependency] = '(1,0.0.0+0)' # No effect for single image boot cmd_wrapper[pos_wrapper_raw_bin] = concat_bin cmd_wrapper[pos_wrapper_signed_bin] = concat_signed_bin retcode = run_cmd(cmd_wrapper, MBED_OS_ROOT) if retcode: raise Exception("Unable to sign " + "concatenated" + " binary, Error code: " + str(retcode)) return #3. Concatenate MCUboot and signed binary flash_area_0_offset = find_flash_area_0_offset(flash_layout) out_ih = IntelHex() out_ih.loadbin(bl2_bin) out_ih.loadbin(concat_signed_bin, flash_area_0_offset) out_ih.tofile(splitext(non_secure_bin)[0] + ".hex", 'hex') out_ih.tobinfile(non_secure_bin) # Generate firmware update file for PSA Firmware Update shutil.copy(concat_signed_bin, update_bin)
def dfu(serial, connect_attempts, detach, dry_run, firmware): """Program via STMicroelectronics DFU interface. Enter dfu mode using `solo program aux enter-dfu` first. """ import time from intelhex import IntelHex import usb.core dfu = solo.dfu.find(serial, attempts=connect_attempts) if dfu is None: print("No STU DFU device found.") if serial is not None: print("Serial number used: ", serial) sys.exit(1) dfu.init() if not dry_run: # The actual programming # TODO: move to `operations.py` or elsewhere ih = IntelHex() ih.fromfile(firmware, format="hex") chunk = 2048 # Why is this unused # seg = ih.segments()[0] size = sum([max(x[1] - x[0], chunk) for x in ih.segments()]) total = 0 t1 = time.time() * 1000 print("erasing...") try: dfu.mass_erase() except usb.core.USBError: # garbage write, sometimes needed before mass_erase dfu.write_page(0x08000000 + 2048 * 10, "ZZFF" * (2048 // 4)) dfu.mass_erase() page = 0 for start, end in ih.segments(): for i in range(start, end, chunk): page += 1 data = ih.tobinarray(start=i, size=chunk) dfu.write_page(i, data) total += chunk # here and below, progress would overshoot 100% otherwise progress = min(100, total / float(size) * 100) sys.stdout.write( "downloading %.2f%% %08x - %08x ... \r" % (progress, i, i + page)) # time.sleep(0.100) # print('done') # print(dfu.read_mem(i,16)) t2 = time.time() * 1000 print() print("time: %d ms" % (t2 - t1)) print("verifying...") progress = 0 for start, end in ih.segments(): for i in range(start, end, chunk): data1 = dfu.read_mem(i, 2048) data2 = ih.tobinarray(start=i, size=chunk) total += chunk progress = min(100, total / float(size) * 100) sys.stdout.write("reading %.2f%% %08x - %08x ... \r" % (progress, i, i + page)) if (end - start) == chunk: assert data1 == data2 print() print("firmware readback verified.") if detach: dfu.prepare_options_bytes_detach() dfu.detach() print("Please powercycle the device (pull out, plug in again)") hot_patch_windows_libusb()
print("ERROR: incorrect arguments") print( "phyihex.py: format an ihex to match exactly physical memory map (pad with 0xFF)" ) print("Usage:") print("phyihex.py <ihex> <start> <end> [<start> <end>]*") exit() ihexf = sys.argv[1] sections = [] for i in range(2, len(sys.argv), 2): start = int(sys.argv[i], 0) end = int(sys.argv[i + 1], 0) sections.append([start, end + 1]) ih = IntelHex() iho = IntelHex() ih.loadhex(ihexf) all_sections = ih.segments() print("input hex file sections:") for sec in all_sections: print("0x%08X 0x%08X" % (sec[0], sec[1] - 1)) #copy all regular sections for sec in sections: for i in range(sec[0], sec[1]): iho[i] = ih[i] #copy start address #print("start address: ",ih.start_addr) iho.start_addr = ih.start_addr
def __init__(self): self.hexinstance = IntelHex()
#use universal address, will program any node in bootloader mode on network. Will probably break if more than one :( CRIS = 0x00 NNB = 0xFF if not selectNode(CRIS, NNB, connection, verbose): exit(1) #connected, now what? #first, verify that this is an AT90CAN128 # selectMemorySpace(SIGNATURE_SPACE, CRIS, connection, verbose) #first, select the Flash memory space # selectMemorySpace(FLASH_SPACE, CRIS, connection, verbose) # selectMemoryPage(0, CRIS, connection, verbose) #now, read the HEX file, and start writing ih = IntelHex(hexfile) address = ih.minaddr() max = ih.maxaddr() if (max > 0xFFFF): #have to do this in two pages max = 0xFFFF #now, start programming eraseMemory(CRIS, connection, verbose) writeMemory(ih, ih.minaddr(), ih.minaddr(), max, CRIS, connection, verbose) #and now, verify verifyMemory(ih, CRIS, connection, verbose) #finally, set bootloader flag, and start application selectMemorySpace(EEPROM_SPACE, CRIS, connection, verbose) writeMemory([0x00, 0x00], 0x00, 0x0FF8, 0x0FF9, CRIS, connection, verbose) frame = makeframestring(CRIS, CAN_DISPLAY_DATA,
def __init__(self, filename): self.memory_map = IntelHex(filename)
def program_hex(self): # Get the nrfjprog command use to actually program self.hex_. self.logger.info('Flashing file: {}'.format(self.hex_)) # What type of erase argument should we pass to nrfjprog? if self.erase: erase_arg = '--chiperase' else: if self.family == 'NRF52': erase_arg = '--sectoranduicrerase' else: erase_arg = '--sectorerase' program_commands = [] if self.family == 'NRF53': # ************** HACK HACK HACK ********************* # NCSDK-7327 workaround # *************************************************** # self.hex_ can contain code for both application core and network # core. Code being flashed to the network core needs to have a # specific argument provided to nrfjprog. If network code is found, # generate two new hex files, one for each core, and flash them # with the correct '--coprocessor' argument. ih = IntelHex(self.hex_) net_hex = IntelHex() app_hex = IntelHex() for s in ih.segments(): if s[0] >= 0x01000000: net_hex.merge(ih[s[0]:s[1]]) else: app_hex.merge(ih[s[0]:s[1]]) wd = os.path.dirname(self.hex_) if len(net_hex) > 0: net_hex_file = os.path.join( wd, 'GENERATED_CP_NETWORK_' + os.path.basename(self.hex_)) self.logger.info( "Generating CP_NETWORK hex file {}".format(net_hex_file)) net_hex.write_hex_file(net_hex_file) program_net_cmd = [ 'nrfjprog', '--program', net_hex_file, erase_arg, '-f', 'NRF53', '--coprocessor', 'CP_NETWORK', '--snr', self.snr ] + self.tool_opt program_commands.append(program_net_cmd) if len(app_hex) > 0: app_hex_file = os.path.join( wd, 'GENERATED_CP_APPLICATION_' + os.path.basename(self.hex_)) self.logger.info( "Generating CP_APPLICATION hex file {}".format( app_hex_file)) app_hex.write_hex_file(app_hex_file) program_app_cmd = [ 'nrfjprog', '--program', app_hex_file, erase_arg, '-f', 'NRF53', '--coprocessor', 'CP_APPLICATION', '--snr', self.snr ] + self.tool_opt program_commands.append(program_app_cmd) else: # It's important for tool_opt to come last, so it can override # any options that we set here. program_commands.append([ 'nrfjprog', '--program', self.hex_, erase_arg, '-f', self.family, '--snr', self.snr ] + self.tool_opt) try: for command in program_commands: self.check_call(command) except subprocess.CalledProcessError as cpe: if cpe.returncode == UnavailableOperationBecauseProtectionError: if self.family == 'NRF53': family_help = ( ' Note: your target is an nRF53; all flash memory ' 'for both the network and application cores will be ' 'erased prior to reflashing.') else: family_help = ( ' Note: this will recover and erase all flash memory ' 'prior to reflashing.') self.logger.error( 'Flashing failed because the target ' 'must be recovered.\n' ' To fix, run "west flash --recover" instead.\n' + family_help) raise
def main(): # pylint: disable=too-many-locals # Set up a simple argument parser. parser = binhoArgumentParser(description="Utility for working with I2C EEPROMs") parser.add_argument( "-u", "--pullup", action="store_true", help="Enable 2.2k pullup resistors (3.3V)", ) parser.add_argument( "-n", "--partnumber", default=None, help="Look up device parameters based on EEPROM manufacturer part number. These parameters can be provided \ individually.", ) parser.add_argument( "-f", "--frequency", default=400000, help="Max supported clock frequency of the EEPROM", ) parser.add_argument( "-a", "--address", default=0, help="Offset from base address set on pins A0-A2, if present on package. Defaults to 0b000", ) parser.add_argument("-c", "--capacity", default=None, type=int, help="EEPROM capacity in bytes") parser.add_argument("-t", "--writetime", default=0.005, type=float, help="EEPROM write cycle time") parser.add_argument( "-m", "--bitmask", default="AAA", help="Bitmask to determine how to use lowest three bits of EEPROM I2C Address", ) parser.add_argument("-g", "--pagesize", default=None, type=int, help="EEPROM page size in bytes") parser.add_argument( "-b", "--blank", action="store_true", help="Check if the EEPROM is blank. No other operation will be performed.", ) parser.add_argument("-e", "--erase", action="store_true", help="Erase the EEPROM before writing") parser.add_argument( "-y", "--verify", action="store_true", help="Verify the EEPROM contents after writing", ) parser.add_argument( "-r", "--read", default=None, type=str, help="Read EEPROM data and save it to the provided file", ) parser.add_argument( "-w", "--write", default=None, type=str, help="Write the data from the provided file to the EEPROM", ) args = parser.parse_args() log_function = log_verbose if args.verbose else log_silent try: log_function("Trying to find a Binho host adapter...") device = parser.find_specified_device() if device.inBootloaderMode: print( "{} found on {}, but it cannot be used now because it's in DFU mode".format( device.productName, device.commPort ) ) sys.exit(errno.ENODEV) elif device.inDAPLinkMode: print( "{} found on {}, but it cannot be used now because it's in DAPlink mode".format( device.productName, device.commPort ) ) print("Tip: Exit DAPLink mode using 'binho daplink -q' command") sys.exit(errno.ENODEV) else: log_function("{} found on {}. (Device ID: {})".format(device.productName, device.commPort, device.deviceID)) except DeviceNotFoundError: if args.serial: print( "No Binho host adapter found matching Device ID '{}'.".format(args.serial), file=sys.stderr, ) else: print("No Binho host adapter found!", file=sys.stderr) sys.exit(errno.ENODEV) # if we fail before here, no connection to the device was opened yet. # however, if we fail after this point, we need to make sure we don't # leave the serial port open. try: t0 = time.time() # pylint: disable=unused-variable ih = IntelHex() # pylint: disable=unused-variable programmer = [] # pylint: disable=unused-variable device.operationMode = "I2C" if args.partnumber: # programmer = device.create_programmer('eeprom', device='24FC512') log_function("Using predefined parameters for EEPROM part number {}".format(args.partnumber.upper())) programmer = device.create_programmer("eeprom", device=args.partnumber.upper()) elif args.frequency and args.capacity and args.writetime and args.bitmask and args.pagesize: log_function("EEPROM manually defined:") log_function( "Max Clock Frequency: {}, Base Address Offset: {}, Bitmask: {}".format( args.frequency, args.address, args.bitmask ) ) log_function( "Capacity: {} bytes, Page Size: {} bytes, Write Cycle Time: {} s".format( args.capacity, args.pagesize, args.writetime ) ) programmer = device.create_programmer( "eeprom", args.capacity, args.pagesize, bitmask=str(args.bitmask), slave_address=args.address, write_cycle_length=args.writetime, ) else: log_function("EEPROM part number not provided, parameters must be manually supplied.") log_function("Flags -f, -a, -c, -t, -m, -g should be used to supply the device parameters") log_function("when the device part number cannot be used.") device.close() sys.exit(1) log_function("") if args.pullup: log_function( "Engaging the internal 2.2kOhm PullUp resistors. (Pulled to 3.3V). Remove the '-u' flag to rely on " "external resistors." ) device.i2c.useInternalPullUps = True else: log_function( "Internal 2.2kOhm PullUp resistors are disengaged. Add the '-u' flag to engage the internal resistors." ) device.i2c.useInternalPullUps = False log_function("") if args.read and args.write: log_function( "Cannot perform read and write in the same operation! Please perform these operations separately" ) device.close() sys.exit(1) if args.verify and not args.write: log_function("Cannot perform verify without writing a file at this time.") device.close() sys.exit(1) if args.blank: log_function("Checking if the EEPROM is blank...") t_start = time.time() isBlank = programmer.blankCheck() t_stop = time.time() elapsedTime = "%.3f" % (t_stop - t_start) if isBlank: log_function("EEPROM is blank! Elapsed time: {} seconds".format(elapsedTime)) device.close() sys.exit(0) else: log_function("EEPROM is NOT blank! Elapsed time: {} seconds".format(elapsedTime)) device.close() sys.exit(1) if args.erase: log_function("Erasing the EEPROM...") te_start = time.time() programmer.erase() te_stop = time.time() elapsedTime = "%.3f" % (te_stop - te_start) log_function("EEPROM Erase completed! Elapsed time: {} seconds".format(elapsedTime)) if args.read: filename, file_extension = os.path.splitext(args.read) # pylint: disable=unused-variable fileFormat = "bin" if file_extension == ".hex": fileFormat = "hex" log_function("Reading from the EEPROM...") tr_start = time.time() programmer.readToFile(args.read, format=fileFormat) tr_stop = time.time() elapsedTime = "%.3f" % (tr_stop - tr_start) log_function("EEPROM Read completed! Elapsed time: {} seconds".format(elapsedTime)) log_function("EEPROM Data saved as {} file to : {}".format(fileFormat, args.read)) if args.write: filename, file_extension = os.path.splitext(args.write) fileFormat = "bin" if file_extension == ".hex": fileFormat = "hex" log_function("Writing Data to EEPROM from {} file: {}".format(fileFormat, args.write)) tw_start = time.time() programmer.writeFromFile(args.write, format=fileFormat) tw_stop = time.time() elapsedTime = "%.3f" % (tw_stop - tw_start) log_function("EEPROM Write completed! Elapsed time: {} seconds".format(elapsedTime)) if args.verify: log_function("Verifying Data written to EEPROM from {} file: {}".format(fileFormat, args.write)) ty_start = time.time() programmer.verifyFile(args.write, format=fileFormat) ty_stop = time.time() elapsedTime = "%.3f" % (ty_stop - ty_start) log_function("EEPROM Verify completed! Elapsed time: {} seconds".format(elapsedTime)) # close the connection to the host adapter device.close() except Exception: # pylint: disable=broad-except # Catch any exception that was raised and display it binho_error_hander() # close the connection to the host adapter device.close()
from intelhex import IntelHex parser = argparse.ArgumentParser( description='Convert Intel hex file to human-readable hex dump') parser.add_argument('input', type=argparse.FileType('r'), help='input file in Intel hex format') parser.add_argument('-o', '--output', type=argparse.FileType('w'), default=sys.stdout, help='hex dump output file') args = parser.parse_args() data = IntelHex().read(args.input, max_size=256) l = len(data) bpl = 16 for la in range(0, l, bpl): if la > 0 and la % (4 * bpl) == 0: print(file=args.output) print('%04x:' % la, file=args.output, end='') for j in range(bpl): if la + j < l: if j > 0 and j % 4 == 0: print(' ', file=args.output, end='') print(' %02x' % data[la + j], file=args.output, end='') print(file=args.output)
def parse_args(): parser = argparse.ArgumentParser( description="Hash data from file.", formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument( "--infile", "-i", "--in", "-in", required=True, help= "Hash the contents of the specified file. If a *.hex file is given, the contents will " "first be converted to binary, with all non-specified area being set to 0xff. " "For all other file types, no conversion is done.") return parser.parse_args() if __name__ == "__main__": args = parse_args() if args.infile.endswith('.hex'): ih = IntelHex(args.infile) ih.padding = 0xff # Allows hashing with empty regions to_hash = ih.tobinstr() else: to_hash = open(args.infile, 'rb').read() sys.stdout.buffer.write(hashlib.sha256(to_hash).digest())
def update_device(device, firmware, logger, cancellation_token): """ Updates the specified device with the specified firmware. The device passed to this function can either be in normal mode or in DFU mode. The firmware should be an instance of Firmware or None. If firmware is None, the newest firmware for the device is downloaded from GitHub releases. """ if isinstance(device, usb.core.Device): serial_number = device.serial_number dfudev = DfuDevice(device) if (logger._verbose): logger.debug("OTP:") dump_otp(dfudev) # Read hardware version from one-time-programmable memory otp_sector = [s for s in dfudev.sectors if s['name'] == 'OTP Memory' and s['addr'] == 0x1fff7800][0] otp_data = dfudev.read_sector(otp_sector) if otp_data[0] == 0: otp_data = otp_data[16:] if otp_data[0] == 0xfe: hw_version = (otp_data[3], otp_data[4], otp_data[5]) else: hw_version = (0, 0, 0) else: serial_number = device.__channel__.usb_device.serial_number dfudev = None # Read hardware version as reported from firmware hw_version_major = device.hw_version_major if hasattr(device, 'hw_version_major') else 0 hw_version_minor = device.hw_version_minor if hasattr(device, 'hw_version_minor') else 0 hw_version_variant = device.hw_version_variant if hasattr(device, 'hw_version_variant') else 0 hw_version = (hw_version_major, hw_version_minor, hw_version_variant) if hw_version < (3, 5, 0): print(" DFU mode is not supported on board version 3.4 or earlier.") print(" This is because entering DFU mode on such a device would") print(" break the brake resistor FETs under some circumstances.") print("Warning: DFU mode is not supported on ODrives earlier than v3.5 unless you perform a hardware mod.") if not odrive.utils.yes_no_prompt("Do you still want to continue?", False): raise OperationAbortedException() fw_version_major = device.fw_version_major if hasattr(device, 'fw_version_major') else 0 fw_version_minor = device.fw_version_minor if hasattr(device, 'fw_version_minor') else 0 fw_version_revision = device.fw_version_revision if hasattr(device, 'fw_version_revision') else 0 fw_version_prerelease = device.fw_version_prerelease if hasattr(device, 'fw_version_prerelease') else True fw_version = (fw_version_major, fw_version_minor, fw_version_revision, fw_version_prerelease) print("Found ODrive {} ({}) with firmware {}{}".format( serial_number, get_hw_version_string(hw_version), get_fw_version_string(fw_version), " in DFU mode" if dfudev is not None else "")) if firmware is None: if hw_version == (0, 0, 0): if dfudev is None: suggestion = 'You have to manually flash an up-to-date firmware to make automatic checks work. Run `odrivetool dfu --help` for more info.' else: suggestion = 'Run "make write_otp" to program the board version.' raise Exception('Cannot check online for new firmware because the board version is unknown. ' + suggestion) print("Checking online for newest firmware...", end='') firmware = get_newest_firmware(hw_version) if firmware is None: raise Exception("could not find any firmware release for this board version") print(" found {}".format(get_fw_version_string(firmware.fw_version))) if firmware.fw_version <= fw_version: print() if firmware.fw_version < fw_version: print("Warning: you are about to flash firmware {} which is older than the firmware on the device ({}).".format( get_fw_version_string(firmware.fw_version), get_fw_version_string(fw_version))) else: print("You are about to flash firmware {} which is the same version as the firmware on the device ({}).".format( get_fw_version_string(firmware.fw_version), get_fw_version_string(fw_version))) if not odrive.utils.yes_no_prompt("Do you want to flash this firmware anyway?", False): raise OperationAbortedException() # load hex file # TODO: Either use the elf format or pack a custom format with a manifest. # This way we can for instance verify the target board version and only # have to publish one file for every board (instead of elf AND hex files). hexfile = IntelHex(firmware.get_as_hex()) logger.debug("Contiguous segments in hex file:") for start, end in hexfile.segments(): logger.debug(" {:08X} to {:08X}".format(start, end - 1)) # Back up configuration if dfudev is None: did_backup_config = device.user_config_loaded if hasattr(device, 'user_config_loaded') else False if did_backup_config: odrive.configuration.backup_config(device, None, logger) elif not odrive.utils.yes_no_prompt("The configuration cannot be backed up because the device is already in DFU mode. The configuration may be lost after updating. Do you want to continue anyway?", True): raise OperationAbortedException() # Put the device into DFU mode if it's not already in DFU mode if dfudev is None: find_odrive_cancellation_token = Event(cancellation_token) put_into_dfu_mode(device, find_odrive_cancellation_token) stm_device = find_device_in_dfu_mode(serial_number, cancellation_token) find_odrive_cancellation_token.set() dfudev = DfuDevice(stm_device) logger.debug("Sectors on device: ") for sector in dfudev.sectors: logger.debug(" {:08X} to {:08X} ({})".format( sector['addr'], sector['addr'] + sector['len'] - 1, sector['name'])) # fill sectors with data touched_sectors = list(populate_sectors(dfudev.sectors, hexfile)) logger.debug("The following sectors will be flashed: ") for sector,_ in touched_sectors: logger.debug(" {:08X} to {:08X}".format(sector['addr'], sector['addr'] + sector['len'] - 1)) # Erase try: for i, (sector, data) in enumerate(touched_sectors): print("Erasing... (sector {}/{}) \r".format(i, len(touched_sectors)), end='', flush=True) dfudev.erase_sector(sector) print('Erasing... done \r', end='', flush=True) finally: print('', flush=True) # Flash try: for i, (sector, data) in enumerate(touched_sectors): print("Flashing... (sector {}/{}) \r".format(i, len(touched_sectors)), end='', flush=True) dfudev.write_sector(sector, data) print('Flashing... done \r', end='', flush=True) finally: print('', flush=True) # Verify try: for i, (sector, expected_data) in enumerate(touched_sectors): print("Verifying... (sector {}/{}) \r".format(i, len(touched_sectors)), end='', flush=True) observed_data = dfudev.read_sector(sector) mismatch_pos = get_first_mismatch_index(observed_data, expected_data) if not mismatch_pos is None: mismatch_pos -= mismatch_pos % 16 observed_snippet = ' '.join('{:02X}'.format(x) for x in observed_data[mismatch_pos:mismatch_pos+16]) expected_snippet = ' '.join('{:02X}'.format(x) for x in expected_data[mismatch_pos:mismatch_pos+16]) raise RuntimeError("Verification failed around address 0x{:08X}:\n".format(sector['addr'] + mismatch_pos) + " expected: " + expected_snippet + "\n" " observed: " + observed_snippet) print('Verifying... done \r', end='', flush=True) finally: print('', flush=True) # If the flash operation failed for some reason, your device is bricked now. # You can unbrick it as long as the device remains powered on. # (or always with an STLink) # So for debugging you should comment this last part out. # Jump to application dfudev.jump_to_application(0x08000000) logger.info("Waiting for the device to reappear...") device = odrive.find_any("usb", serial_number, cancellation_token, cancellation_token, timeout=30) if did_backup_config: odrive.configuration.restore_config(device, None, logger) os.remove(odrive.configuration.get_temp_config_filename(device)) logger.success("Device firmware update successful.")
sys.exit(1) if not os.path.isfile(binfile): print("Unreadable file '%s'." % binfile) sys.exit(1) checkbin(binfile) target.append({ 'address': address, 'data': open(binfile, 'rb').read() }) if options.hexfiles: if not IntelHex: print("Error: IntelHex python module could not be found") sys.exit(1) for hex in options.hexfiles: ih = IntelHex(hex) for (address, end) in ih.segments(): try: address = address & 0xFFFFFFFF except ValueError: print("Address %s invalid." % address) sys.exit(1) target.append({ 'address': address, 'data': ih.tobinstr(start=address, end=end - 1) }) outfile = args[0] device = DEFAULT_DEVICE
def main(argv): if (len(argv) != 2 and len(argv) != 3): print('Usage: otam_hex.py fw.hex [resource.hex]') return fname = argv[1] fname_res = None if (len(argv) == 3): fname_res = argv[2] try: ih = IntelHex(fname) ih = None ih_res = None if (fname_res is not None): ih_res = IntelHex(fname) ih_res = None except: print('Open hex file failed:', fname, 'or resouce file error:', fname_res) print('Usage: otam_hex.py fw.hex [resource.hex]') return ih = hexf(fname) ih_res = [] if (fname_res is not None): ih_res = hexf(fname_res) is_res = True for ihp in ih: if (ihp[0] == 0x1fff4000 or ihp[0] == 0x1fff4800): is_res = False break fname_bin = fname + '.bin' fp = open(fname_bin, 'wb') try: tag = 'OTAF'.encode() if (is_res): tag = 'OTAR'.encode() fp.write(tag) lval = [0, 0, 0, 0] lval[0] = (len(ih) + len(ih_res)) & 0xff fp.write(bytes(lval)) for ihp in ih: #run address param = ihp[0] lval = [0, 0, 0, 0] lval[0] = param & 0xff lval[1] = (param >> 8) & 0xff lval[2] = (param >> 16) & 0xff lval[3] = (param >> 24) & 0xff fp.write(bytes(lval)) #partition size param = len(ihp[1]) lval = [0, 0, 0, 0] lval[0] = param & 0xff lval[1] = (param >> 8) & 0xff lval[2] = (param >> 16) & 0xff lval[3] = (param >> 24) & 0xff fp.write(bytes(lval)) for ihp in ih_res: #run address param = ihp[0] lval = [0, 0, 0, 0] lval[0] = param & 0xff lval[1] = (param >> 8) & 0xff lval[2] = (param >> 16) & 0xff lval[3] = (param >> 24) & 0xff fp.write(bytes(lval)) #partition size param = len(ihp[1]) lval = [0, 0, 0, 0] lval[0] = param & 0xff lval[1] = (param >> 8) & 0xff lval[2] = (param >> 16) & 0xff lval[3] = (param >> 24) & 0xff fp.write(bytes(lval)) for ihp in ih: fp.write(bytes(ihp[1])) for ihp in ih_res: fp.write(bytes(ihp[1])) fp.close() except: print('Open hex file failed:', fname) print('Usage: otam_hex.py fw.hex')
opts, args = getopt.gnu_getopt(argv, 'hv', ['help', 'version']) for o,a in opts: if o in ('-h', '--help'): print(USAGE) return 0 elif o in ('-v', '--version'): print(VERSION) return 0 except getopt.GetoptError, e: sys.stderr.write(str(e)+"\n") sys.stderr.write(USAGE+"\n") return 1 if len(args) != 2: sys.stderr.write("ERROR: You should specify 2 files to diff.\n") sys.stderr.write(USAGE+"\n") return 1 fname1, fname2 = args from intelhex import IntelHex, diff_dumps ih1 = IntelHex(fname1) ih2 = IntelHex(fname2) diff_dumps(ih1, ih2, name1=fname1, name2=fname2) if __name__ == '__main__': sys.exit(main())
pic18f2_4xk22 = PIC18F2_4XK22() AllCpuFamily = [pic12, pic18fxx2, pic18f2xxx, pic18fxxk80, pic18f2_4xk22] #============= main ========== if __name__ == '__main__': if len(sys.argv) is 2: HexFile = sys.argv[1] elif len(sys.argv) is 1: HexFile = '' else: print 'Usage: %s file.hex' % sys.argv[0] quit() ## load hex file if it exist FileData = IntelHex() if len(HexFile) > 0: try: FileData.loadhex(HexFile) except IOError: print 'Error in file "', HexFile, '"' quit() PicData = FileData.todict() print 'File "', HexFile, '" loaded' #try to figure out the CpuId by scanning all available Cpu family IO.Setup_Interface() CpuId = 0 print "Scan CPU "
def load(self, path): ih = IntelHex(path) return ih.tobinarray(), ih.minaddr()
def __init__(self, path): """ Read a firmware file and store its data ready for device programming. This class will try to guess the file type if python-magic is available. If python-magic indicates a plain text file, and if IntelHex is available, then the file will be treated as one of Intel HEX format. In all other cases, the file will be treated as a raw binary file. In both cases, the file's contents are stored in bytes for subsequent usage to program a device or to perform a crc check. Parameters: path -- A str with the path to the firmware file. Attributes: bytes: A bytearray with firmware contents ready to send to the device """ self._crc32 = None firmware_is_hex = False if have_magic: file_type = bytearray(magic.from_file(path, True)) # from_file() returns bytes with PY3, str with PY2. This comparison # will be True in both cases""" if file_type == b'text/plain': firmware_is_hex = True mdebug(5, "Firmware file: Intel Hex") elif file_type == b'application/octet-stream': mdebug(5, "Firmware file: Raw Binary") else: error_str = "Could not determine firmware type. Magic " \ "indicates '%s'" % (file_type) raise CmdException(error_str) else: if os.path.splitext(path)[1][1:] in self.HEX_FILE_EXTENSIONS: firmware_is_hex = True mdebug(5, "Your firmware looks like an Intel Hex file") else: mdebug(5, "Cannot auto-detect firmware filetype: Assuming .bin") mdebug( 10, "For more solid firmware type auto-detection, install " "python-magic.") mdebug(10, "Please see the readme for more details.") if firmware_is_hex: if have_hex_support: self.bytes = bytearray(IntelHex(path).tobinarray()) return else: error_str = "Firmware is Intel Hex, but the IntelHex library " \ "could not be imported.\n" \ "Install IntelHex in site-packages or program " \ "your device with a raw binary (.bin) file.\n" \ "Please see the readme for more details." raise CmdException(error_str) with open(path, 'rb') as f: self.bytes = bytearray(f.read())
# Copyright (C) 2015 Herbert Poetzl import sys import serial import struct from intelhex import IntelHex from smbus import SMBus from time import sleep from axiom_icsp import * tty = "/dev/ttyPS1" sel = sys.argv[1] if len(sys.argv) > 1 else "A" ver = "0.36" ih = IntelHex(sys.argv[2]) ih.padding = 0xFF def ih_data(ih, addr, count=32): data = [] mask = 0xFFFF for n in range(count): l, h = ih[(addr + n) * 2], ih[(addr + n) * 2 + 1] val = (h << 8) | l mask &= val data.append(val) return data, mask ser = serial.Serial(
def binary_hook(t_self, resources, _, binf): """Hook that merges the soft device with the bin file""" # Scan to find the actual paths of soft device sdf = None for softdevice_and_offset_entry\ in t_self.target.EXPECTED_SOFTDEVICES_WITH_OFFSETS: for hexf in resources.get_file_paths(FileType.HEX): if hexf.find(softdevice_and_offset_entry['name']) != -1: t_self.notify.debug("SoftDevice file found %s." % softdevice_and_offset_entry['name']) sdf = hexf if sdf is not None: break if sdf is not None: break if sdf is None: t_self.notify.debug("Hex file not found. Aborting.") return # Look for bootloader file that matches this soft device or bootloader # override image blf = None if t_self.target.MERGE_BOOTLOADER is True: for hexf in resources.get_file_paths(FileType.HEX): if hexf.find(t_self.target.OVERRIDE_BOOTLOADER_FILENAME) != -1: t_self.notify.debug( "Bootloader file found %s." % t_self.target.OVERRIDE_BOOTLOADER_FILENAME) blf = hexf break elif hexf.find(softdevice_and_offset_entry['boot']) != -1: t_self.notify.debug("Bootloader file found %s." % softdevice_and_offset_entry['boot']) blf = hexf break # Merge user code with softdevice from intelhex import IntelHex binh = IntelHex() _, ext = os.path.splitext(binf) if ext == ".hex": binh.loadhex(binf) elif ext == ".bin": binh.loadbin(binf, softdevice_and_offset_entry['offset']) if t_self.target.MERGE_SOFT_DEVICE is True: t_self.notify.debug("Merge SoftDevice file %s" % softdevice_and_offset_entry['name']) sdh = IntelHex(sdf) sdh.start_addr = None binh.merge(sdh) if t_self.target.MERGE_BOOTLOADER is True and blf is not None: t_self.notify.debug("Merge BootLoader file %s" % blf) blh = IntelHex(blf) blh.start_addr = None binh.merge(blh) with open(binf.replace(".bin", ".hex"), "w") as fileout: binh.write_hex_file(fileout, write_start_addr=False)
def bundlePackAndSign(bundleName, firmwareName, oldAesKey, newAesKey, updateFileName, verbose): # Rather than at the beginning of the files, constants are here HASH_LENGH = 128 / 8 # Hash length (128 bits) AES_KEY_LENGTH = 256 / 8 # AES key length (256 bits) FW_VERSION_LENGTH = 4 # Length of the firmware version in the bundle AES_KEY_UPDATE_FLAG_LGTH = 1 # Length of the tag which specifies a firmware udpate FW_MAX_LENGTH = 28672 # Maximum firmware length, depends on size allocated to bootloader FLASH_SECTOR_0_LENGTH = 264 * 8 # Length in bytes of sector 0a in external flash (to change for 16Mb & 32Mb flash!) STORAGE_SPACE = 65536 - FLASH_SECTOR_0_LENGTH # Uint16_t addressing space - sector 0a length (dedicated to other storage...) BUNDLE_MAX_LENGTH = STORAGE_SPACE - FW_MAX_LENGTH - HASH_LENGH - AES_KEY_LENGTH - FW_VERSION_LENGTH - AES_KEY_UPDATE_FLAG_LGTH # Robust RNG rng = Random.new() # Extracted firmware version firmware_version = None # AES Key Update Bool aes_key_update_bool = True # Check that all required files are here if isfile(bundleName): if verbose == True: print "Bundle file found" else: print "Couldn't find bundle file" return False if isfile(firmwareName): if verbose == True: print "Firmware file found" else: print "Couldn't find firmware file" return False # Read bundle and firmware data firmware = IntelHex(firmwareName) firmware_bin = firmware.tobinarray() fd = open(bundleName, 'rb') bundle = fd.read() fd.close() # Check that the firmware data actually starts at address 0 if firmware.minaddr() != 0: print "Firmware start address isn't correct" return False # Check that the bundle & firmware data aren't bigger than they should be if len(bundle) > BUNDLE_MAX_LENGTH: print "Bundle file too long:", len(bundle), "bytes long" return False else: if verbose == True: print "Bundle file is", len(bundle), "bytes long" if len(firmware) > FW_MAX_LENGTH: print "Firmware file too long:", len(firmware), "bytes long" return False else: if verbose == True: print "Firmware file is", len(firmware), "bytes long" if verbose == True: print "Remaining space in MCU flash:", FW_MAX_LENGTH - len( firmware), "bytes" if verbose == True: print "Remaining space in bundle:", STORAGE_SPACE - FW_MAX_LENGTH - HASH_LENGH - AES_KEY_LENGTH - FW_VERSION_LENGTH - AES_KEY_UPDATE_FLAG_LGTH - len( bundle), "bytes" # Beta testers devices have their aes key set to 00000... and the bootloader will always perform a key update if oldAesKey == "0000000000000000000000000000000000000000000000000000000000000000" and newAesKey == None: if verbose == True: print "Bundle update for beta testers unit, setting 00000... as new AES key" newAesKey = "0000000000000000000000000000000000000000000000000000000000000000" # If no new aes key is specified, don't set the aes key update flag if newAesKey == None: if verbose == True: print "No new AES key set" aes_key_update_bool = False else: if verbose == True: print "Encrypting new AES key" # If needed, check the new aes key if aes_key_update_bool == True: new_aes_key = array('B', newAesKey.decode("hex")) if len(new_aes_key) != AES_KEY_LENGTH: print "Wrong New AES Key Length:", len(new_aes_key) return False # Convert & check the old aes key old_aes_key = array('B', oldAesKey.decode("hex")) if len(old_aes_key) != AES_KEY_LENGTH: print "Wrong Old AES Key Length:", len(oldAesKey) return False # Get version number for i in range(len(firmware_bin) - 3): if chr(firmware_bin[i]) == 'v' and \ chr(firmware_bin[i + 1]) >= '1' and chr(firmware_bin[i + 1]) <= '9' and \ chr(firmware_bin[i + 2]) == '.' and \ chr(firmware_bin[i + 3]) >= '0' and chr(firmware_bin[i + 3]) <= '9': firmware_version = firmware_bin[i:i + 4] if verbose == True: print "Extracted firmware version:", "".join( chr(firmware_version[j]) for j in range(0, 4)) break # Check if we extracted the firmware version and it has the correct length if firmware_version == None or len(firmware_version) != FW_VERSION_LENGTH: print "Problem while extracting firmware version" return False # If needed, encrypt the new AES key with the old one if aes_key_update_bool == True: cipher = AES.new(old_aes_key, AES.MODE_ECB, array('B', [0] * AES.block_size)) # IV ignored in ECB enc_password = cipher.encrypt(new_aes_key) if len(enc_password) != AES_KEY_LENGTH: print "Encoded password is too long!" return False else: enc_password = [255] * AES_KEY_LENGTH # Generate beginning of update file data: bundle | padding | firmware version | new aes key bool | firmware | padding | new aes key encoded update_file_data = array('B') update_file_data.extend(bytearray(bundle)) update_file_data.extend( array('B', [0] * (STORAGE_SPACE - HASH_LENGH - AES_KEY_LENGTH - FW_MAX_LENGTH - FW_VERSION_LENGTH - AES_KEY_UPDATE_FLAG_LGTH - len(bundle)))) update_file_data.extend(firmware_version) if aes_key_update_bool == True: update_file_data.append(255) else: update_file_data.append(0) update_file_data.extend(firmware.tobinarray()) update_file_data.extend( array('B', [0] * (STORAGE_SPACE - HASH_LENGH - AES_KEY_LENGTH - len(update_file_data)))) update_file_data.extend(bytearray(enc_password)) # Check length if len(update_file_data) != (STORAGE_SPACE - HASH_LENGH): print "Problem with update file length!" return False # Generate CBCMAC, IV is ZEROS cipher = AES.new(old_aes_key, AES.MODE_CBC, array('B', [0] * AES.block_size)) cbc_mac = cipher.encrypt(update_file_data)[-AES.block_size:] # Append it to update file data: bundle | padding | firmware version | new aes key bool | firmware | padding | new aes key encoded | cbcmac update_file_data.extend(bytearray(cbc_mac)) # Check length if len(update_file_data) != STORAGE_SPACE: print "Problem with update file length!" return False # Write our update image file data_fd = open(updateFileName, 'wb') data_fd.write(update_file_data) data_fd.close() if verbose == True: print "Update file written!" return True
def generateFlashAndEepromHex(originalFlashHexName, bootloaderHexName, serialNumber, AESKey1, AESKey2, UIDKey, UID, newFlashHexName, newEeepromHex, verbose): FW_MAX_LENGTH = 28672 BL_MAX_LENGTH = 4096 AES_KEY_LENGTH = 256 / 8 AES_BLOCK_LENGTH = 128 / 8 UID_REQUEST_KEY_LENGTH = 16 UID_KEY_LENGTH = 6 # Merged bootloader and firmware sha1 hash merged_fw_bl_sha1_hash = "" # Check for original firmware file presence if not os.path.isfile(originalFlashHexName): print "Couldn't find firmware hex file", originalFlashHexName return [False, merged_fw_bl_sha1_hash] # Check for bootloader file presence if not os.path.isfile(bootloaderHexName): print "Couldn't find bootloader hex file", bootloaderHexName return [False, merged_fw_bl_sha1_hash] # Check AES Key Length if len(AESKey1) != AES_KEY_LENGTH: print "Wrong AES Key1 length!" return [False, merged_fw_bl_sha1_hash] # Check AES Key Length if len(AESKey2) != AES_KEY_LENGTH: print "Wrong AES Key2 length!" return [False, merged_fw_bl_sha1_hash] # Check UID Req Key Length if len(UIDKey) != UID_REQUEST_KEY_LENGTH: print "Wrong UID request key length!" return [False, merged_fw_bl_sha1_hash] # Check UID Key Length if len(UID) != UID_KEY_LENGTH: print "Wrong UID key length!" return [False, merged_fw_bl_sha1_hash] # Read firmware Hex flashHex = IntelHex(originalFlashHexName) if len(flashHex) > FW_MAX_LENGTH: print "Firmware file too long:", len(flashHex), "bytes long" return [False, merged_fw_bl_sha1_hash] else: if verbose == True: print "Firmware file is", len(flashHex), "bytes long" # Get fw version number firmware_bin = flashHex.tobinarray() for i in range(len(firmware_bin) - 3): if chr(firmware_bin[i]) == 'v' and \ chr(firmware_bin[i + 1]) >= '1' and chr(firmware_bin[i + 1]) <= '9' and \ chr(firmware_bin[i + 2]) == '.' and \ chr(firmware_bin[i + 3]) >= '0' and chr(firmware_bin[i + 3]) <= '9': firmware_version = firmware_bin[i:i + 4] if verbose == True: print "Extracted firmware version:", "".join( chr(firmware_version[j]) for j in range(0, 4)) break # Check there's nothing where we want to put the fw version number if flashHex[FW_MAX_LENGTH - 4] != 0xFF: print "No space to write fw version number inside the firmware hex!" return [False, merged_fw_bl_sha1_hash] # Write the fw version number in the last 4 bytes of the firmware hex flashHex[FW_MAX_LENGTH - 4] = firmware_version[0] flashHex[FW_MAX_LENGTH - 3] = firmware_version[1] flashHex[FW_MAX_LENGTH - 2] = firmware_version[2] flashHex[FW_MAX_LENGTH - 1] = firmware_version[3] # Read bootloader hex bootloaderHex = IntelHex(bootloaderHexName) if len(bootloaderHex) > BL_MAX_LENGTH: print "Bootloader file too long:", len(bootloaderHex), "bytes long" return [False, merged_fw_bl_sha1_hash] else: if verbose == True: print "Bootloader file is", len(bootloaderHex), "bytes long" # Merge firmware with bootloader flashHex.merge(bootloaderHex) # Generate hash, print it if need merged_fw_bl_sha1_hash = hashlib.sha1(flashHex.tobinarray()).hexdigest() if verbose == True: print "Original Firmware/Bootloader Hash:", merged_fw_bl_sha1_hash # Check there's nothing where we want to put the serial number if flashHex[0x7F7C] != 0xFF: print "No space to write serial number inside the bootloader hex!" return [False, merged_fw_bl_sha1_hash] # Include serial number in the hex to be flashed flashHex[0x7F7C] = (serialNumber >> 24) & 0x000000FF flashHex[0x7F7D] = (serialNumber >> 16) & 0x000000FF flashHex[0x7F7E] = (serialNumber >> 8) & 0x000000FF flashHex[0x7F7F] = (serialNumber >> 0) & 0x000000FF # Write production firmware file flashHex.tofile(newFlashHexName, format="hex") # Generate blank eeprom file eepromHex = IntelHex() for i in range(0x400): eepromHex[i] = 0xFF # Modify the byte in eeprom to indicate normal boot # Address 0: 0xDEAD, little endian eepromHex[0] = 0xAD eepromHex[1] = 0xDE # Address 2: 0xAB (bootloader password set) eepromHex[2] = 0xAB # Address 98: mass production boolean eepromHex[98] = 0xCD # Address 999: boolean specifiying that uid key is set eepromHex[999] = 0xBB # Address 1000: UID request key eepromHex[1000:1000 + UID_REQUEST_KEY_LENGTH] = UIDKey # Address 1016: UID key eepromHex[1000 + UID_REQUEST_KEY_LENGTH:1000 + UID_REQUEST_KEY_LENGTH + UID_KEY_LENGTH] = UID # Address 0x03: 32 bytes of AES key 1 + 30 first bytes of AES key 2 eepromHex[3:3 + AES_KEY_LENGTH] = AESKey1 eepromHex[3 + AES_KEY_LENGTH:3 + AES_KEY_LENGTH + 30] = AESKey2[0:30] # Last 2 bytes in EEPROM: last 2 AESKey 2 bytes eepromHex[0x400 - 2] = AESKey2[30] eepromHex[0x400 - 1] = AESKey2[31] # Write new eeprom file eepromHex.tofile(newEeepromHex, format="hex") # Return success and hash return [True, merged_fw_bl_sha1_hash]
if device_info['flash_kb'] < 64: send_page_data(hid_device, addr, page_data) else: send_page_data(hid_device, addr >> 8, page_data) # Once programming is complete, start the application via a dummy page # program to the page address 0xFFFF print("Programming complete, starting application.") send_page_data(hid_device, 0xFFFF, [0] * device_info['page_size']) finally: hid_device.close() if __name__ == '__main__': # Load the specified HEX file try: hex_data = IntelHex(sys.argv[2]) except: print("Could not open the specified HEX file.") sys.exit(1) # Retrieve the device information entry for the specified device try: device_info = device_info_map[sys.argv[1]] except: print("Unknown device name specified.") sys.exit(1) program_device(hex_data, device_info)
def cmd_update(args): if args.reset_tty: sys.stdout.write("resetting to bootloader via serial\n") serial = Tl866Driver(args.reset_tty) serial.cmd_reset_to_bootloader() # wait for the device to show up devs = None stop_time = time.time() + 5 # timeout 5s while not devs and time.time() < stop_time: time.sleep(0.100) devs = driver.list_devices() dev = find_dev() report = dev.report() if report.status != dev.STATUS_BOOTLOADER: sys.stdout.write("resetting to bootloader\n") dev.reset() report = dev.report() if report.status != dev.STATUS_BOOTLOADER: sys.stderr.write("device did not reset to booloader\n") sys.exit(2) model_a = (report.model == dev.MODEL_TL866A) # load the firmware image stock = None image = None if args.stock: with open(args.firmware, 'rb') as source: stock = firmware.UpdateFile(source.read()) image = firmware.Firmware( stock.a_firmware if model_a else stock.cs_firmware, encrypted=True, ) else: with open(args.firmware, 'r') as source: hexfile = IntelHex(source) image = firmware.Firmware( hexfile.tobinstr(start=0x1800, end=0x1FBFF), encrypted=False, ) # load encryption and erase keys target_key = None target_erase = None if args.keys_from: with open(args.keys_from, 'rb') as source: keyfile = firmware.UpdateFile(source.read()) keyfw = firmware.Firmware( keyfile.a_firmware if model_a else keyfile.cs_firmware, encrypted=True, ) target_key = keyfw.key target_erase = keyfile.a_erase if model_a else keyfile.cs_erase elif stock: target_key = image.key target_erase = stock.a_erase if model_a else stock.cs_erase else: target_key = firmware.KEY_A if model_a else firmware.KEY_CS target_erase = firmware.ERASE_A if model_a else firmware.ERASE_CS if not image.valid: raise RuntimeError("firmware image invalid") sys.stdout.write("erasing\n") dev.erase(target_erase) sys.stdout.write("programming\n") addr = 0x1800 cryptbuf = image.encrypt(target_key) for off in range(0, len(cryptbuf), 80): dev.write(addr, 80, cryptbuf[off:off + 80]) addr += 64 sys.stdout.write("success!\n") report = dev.report() if report.status != dev.STATUS_NORMAL: sys.stdout.write("resetting to firmware\n") dev.reset()