def handle_qspi_config(header_config_path, flash_config_path): qspi_config = ProgramQspiConfig(config_path=header_config_path) if qspi_config.is_valid(): if flash_config_path is not None: if not qspi_config.check_flash_config(flash_config_path): if ui.ask(text="Current flash config isn't listed on supported flash config list " "({})\n Do you want to run configurator?\n" .format(flash_config_path), confirmation='Run', denial='Ignore'): program_qspi_config(header_config_path=header_config_path, flash_config_path=flash_config_path) else: # invalid qspi config if header_config_path is None: if ui.ask(text='Invalid configuration file: {}\n Do you want to run configurator?\n' .format(header_config_path), confirmation='Run', denial='Abort'): program_qspi_config(header_config_path=header_config_path, flash_config_path=flash_config_path) qspi_config.load() else: ui.print_message("Aborted.\n") raise ExecutionCanceled() if not qspi_config.is_valid(): raise RuntimeError("QSPI config is invalid.\nExecution aborted.\n") return qspi_config
def _setup_address(value_name, ui_message): error_msg = "(Previous value was invalid - not a hex number)" while True: user_address = ui.ask_value(value_name=value_name, text=ui_message, default='0x2000') pattern = re.compile("0x[0-9a-fA-F]+") if user_address and pattern.fullmatch(user_address) is None: if not error_msg in ui_message: ui_message += "\n{}".format(error_msg) continue try: final_address = int(user_address[2:], 16) except: ui.print_message("Execution aborted.\n") raise ExecutionCanceled() if final_address >= MIN_FW_ADDRESS: break else: ui.info( "FW cannot be written at address lower than 0x{:X} (0x{:X} given)" .format(MIN_FW_ADDRESS, final_address)) return final_address
def __print_report(report): ui.print_header('Script execution report') for info, status in report.items(): details = [] if isinstance(status, list): details = status[1:] status = status[0] ui.print_message('{}... {}'.format(info, status), ignore_verbose=True) for d in details: ui.print_message('\t{}'.format(d), ignore_verbose=True)
def run_script(callback, argument_parser, suppress_errors=False): ui.set_suppress_errors(suppress_errors) try: callback(*argument_parser()) except RuntimeError as e: ui.error(str(e)) except ApplicationError as e: ui.error(str(e), details='call: ' + e.call) except ExecutionCanceled: ui.print_message('Script execution canceled by user.') finally: kill_jlink_gdb_server()
def select_serial_port(): def find_serial_ports(): serial_list = [] if is_win(): from winreg import OpenKey, HKEY_LOCAL_MACHINE, EnumValue def enumerate_values(key): i = 0 while True: try: yield EnumValue(key, i) i += 1 except WindowsError: break with OpenKey(HKEY_LOCAL_MACHINE, 'HARDWARE\\DEVICEMAP\\SERIALCOMM') as key: for value in enumerate_values(key): if re.match(r'COM\d+', value[1]): serial_list.append(value[1]) elif is_linux(): for value in os.listdir('/dev/'): if re.match(r'tty(USB|ACM)\d+', value): serial_list.append(os.path.join('/', 'dev', value)) else: pass return serial_list serials = find_serial_ports() if not serials: raise RuntimeError( 'No devices found. Please connect at least one and retry.') if len(serials) == 1: return serials[0] device = ui.select_item(item_list=serials, text='Select serial port') if not device: ui.print_message("No valid device selected.\nExecution aborted.\n") raise ExecutionCanceled() return device
def select_jlink_serial(jlink_path): devices = JLinkExe(jlink_path).find_jlink_numbers() if not devices: raise RuntimeError( 'No devices found. Please connect at least one and retry.') if len(devices) == 1: return devices[0] device = ui.select_item(item_list=devices, text='Select jlink serial') if not device: ui.print_message("No valid device selected.\nExecution aborted.\n") raise ExecutionCanceled() return device
def program_qspi(image_file, cli_programmer, serial=None, prog_prod_header=False, header_config_path=None, flash_config_path=None): qspi_config = handle_qspi_config(header_config_path, flash_config_path) if qspi_config.product_id == ProductId.DA1469x_00: if image_file is not None: ui.print_title("Programming image") if qspi_config.update_image_address < MIN_FW_ADDRESS: raise RuntimeError( "FW cannot be written at address lower than 0x{:X} (0x{:X} given)".format( MIN_FW_ADDRESS, qspi_config.update_image_address)) try: version_file = tempfile.mktemp() make_sw_version_file(version_file) try: output_image = tempfile.mktemp() Mkimage().da1469x(image_file, version_file, output_image) cli_programmer.write_qspi(qspi_config.update_image_address, output_image, serial_port=serial, silent=False) finally: os.remove(output_image) finally: os.remove(version_file) if prog_prod_header: ui.print_title("Programming product header") ui.print_message("Using configuration:\n" + qspi_config.get_config_info()) program_product_header(qspi_config, cli_programmer, serial) elif qspi_config.product_id == ProductId.DA14681_01 or \ qspi_config.product_id == ProductId.DA14683_00: if prog_prod_header: ui.print_message("Product header can be programmed only on DA1469x-00 boards. Skipped.") if image_file is not None: ui.print_title("Programming image") cli_programmer.write_qspi_exec(image_file=image_file, serial_port=serial, silent=False) else: raise RuntimeError("No supported device selected ({})".format(qspi_config.product_id)) if image_file is None and prog_prod_header is False: ui.print_message("No image, no header selected. Nothing to do.")
def secure_keys_prog(secure_cfg_file, keys_file, cli_programmer, serial=None): # Report contains all steps in the order of their execution report = OrderedDict([(STEP_CONFIG_FILE_CHECK, STATUS_NOT_RUN), (STEP_CHECK_PROD_ID, STATUS_NOT_RUN), (STEP_KEY_VERIFICATION, STATUS_NOT_RUN), (STEP_OTP_CHECK, STATUS_NOT_RUN), (STEP_KEY_MATCHING, STATUS_NOT_RUN), (STEP_KEY_WRITING, STATUS_NOT_RUN), (STEP_READ_REV_KEYS, STATUS_NOT_RUN), (STEP_CS_WRITE, STATUS_NOT_RUN)]) if secure_cfg_file is None: secure_cfg_file = DEFAULT_SECURE_CFG_FILE if keys_file is None: keys_file = DEFAULT_PRODUCT_KEYS_FILE if not os.path.exists(secure_cfg_file) or not os.path.exists(keys_file): secure_keys_cfg(configuration_file=secure_cfg_file, keys_file=keys_file, prod_id=ProductId.DA1469x_00) secure_cfg = SecurityConfig(secure_cfg_file) product_keys = ProductKeys(keys_file) if not secure_cfg.is_valid(): msg = 'Secure configuration is not valid.' report[STEP_CONFIG_FILE_CHECK] = [STATUS_FAIL, msg] __print_report(report) raise RuntimeError(msg) if not product_keys.is_valid(secure_cfg.product_id): msg = 'Product keys file is not valid.' report[STEP_CONFIG_FILE_CHECK] = [STATUS_FAIL, msg] __print_report(report) raise RuntimeError(msg) report[STEP_CONFIG_FILE_CHECK] = STATUS_PASS ui.print_message('Using product keys file: {}'.format( os.path.normpath(keys_file))) ui.print_message('Using secure configuration file: {}'.format( os.path.normpath(secure_cfg_file))) if secure_cfg.product_id != ProductId.DA1469x_00: report[STEP_CHECK_PROD_ID] = [ STATUS_FAIL, 'Passed ID: {}'.format(secure_cfg.product_id) ] __print_report(report) raise RuntimeError( 'This script could be run only for {} ({} selected)'.format( ProductId.DA1469x_00.value, secure_cfg.product_id)) report[STEP_CHECK_PROD_ID] = STATUS_PASS ui.print_title('Programming product keys') write_product_keys(cli_programmer, serial, product_keys, report) ui.print_title('Checking key revocation status') status = __read_keys_revocation_status(cli_programmer, serial) report[STEP_READ_REV_KEYS] = \ [STATUS_PASS, 'Revoked signature keys: {}'.format(status[AREA_ADDRESS_SIGNATURE_KEYS_INDEX]), 'Revoked user data keys: {}'.format(status[AREA_ADDRESS_USER_DATA_ENCRYPTION_KEYS_INDEX]), 'Revoked FW decryption keys: {}'.format(status[AREA_ADDRESS_FW_DECRYPTION_KEYS_INDEX])] if secure_cfg.cs_enable_secure_boot is True: ui.print_title('Programming SECURE_BOOT_REG in configuration script') if report[STEP_KEY_VERIFICATION] != STATUS_PASS: report[STEP_CS_WRITE] = [ STATUS_NOT_RUN, 'Product keys are not valid.' ] elif report[STEP_KEY_MATCHING] == STATUS_FAIL: report[STEP_CS_WRITE] = [ STATUS_NOT_RUN, 'Product keys from file do not match to the ' 'keys read from OTP.' ] elif report[STEP_KEY_WRITING] == STATUS_FAIL: report[STEP_CS_WRITE] = [ STATUS_NOT_RUN, 'Product keys writing failed.' ] else: report[STEP_CS_WRITE] = write_configuration_script( cli_programmer, serial) else: report[STEP_CS_WRITE] = [ STATUS_NOT_RUN, 'Feature not enabled in configuration' ] __print_report(report)
def prepare_flash_image(bin_file, image_file=None, sw_version_file=None, header_config_path=None, flash_config_path=None): qspi_config = handle_qspi_config(header_config_path, flash_config_path) if qspi_config.product_id != ProductId.DA1469x_00: raise RuntimeError( "This script is not available for {} devices.".format( qspi_config.product_id)) if qspi_config.update_image_address < MIN_FW_ADDRESS: raise RuntimeError( "FW cannot be placed at address lower than 0x{:X} (0x{:X} " "given)".format(MIN_FW_ADDRESS, qspi_config.update_image_address)) if not os.path.exists(bin_file): raise RuntimeError( "Application binary file path is invalid: {}".format(bin_file)) if sw_version_file is not None and not os.path.exists(bin_file): raise RuntimeError( "SW version file path is invalid: {}".format(sw_version_file)) # Product header at 0 + copy of product header at 0x1000 = 0x2000 bytes product_header = make_product_header( qspi_config.active_image_address, qspi_config.update_image_address, qspi_config.flash_burstcmda_reg_value, qspi_config.flash_burstcmdb_reg_value, qspi_config.flash_write_config_command) image_data = product_header * 2 padding_size = qspi_config.update_image_address - len(image_data) if padding_size < 0: # This shouldn't happen - PH + PH copy should have exactly 0x2000 bytes raise RuntimeError( "Calculated offset is invalid: {}".format(padding_size)) image_data += PADDING_VAL * padding_size try: app_mkimage_file = tempfile.mktemp() if sw_version_file is None: try: version_file = tempfile.mktemp() make_sw_version_file(version_file) Mkimage().da1469x(bin_file, version_file, app_mkimage_file) finally: os.remove(version_file) else: Mkimage().da1469x(bin_file, sw_version_file, app_mkimage_file) image_obj = open(app_mkimage_file, "rb") image_data += image_obj.read() image_obj.close() finally: os.remove(app_mkimage_file) if image_file is None: image_file = bin_file + ".flash_img" image_file = os.path.normpath(image_file) with open(image_file, "wb") as image_obj: image_obj.write(image_data) ui.print_message("Storing FLASH image in {}".format(image_file))
def generate_keys(output_file=None, elliptic_curve=None, prod_id=None): if not output_file: output_file = DEFAULT_PRODUCT_KEYS_FILE # Use Enum objects instead of string try: product_id = ProductId(prod_id) except ValueError: raise RuntimeError( 'Invalid or no Product ID passed. Execution aborted.') if product_id == ProductId.DA14681_01: raise RuntimeError('Secure boot feature is not supported by ' + str(product_id.value)) if not os.path.exists(MKIMAGE_BIN): raise RuntimeError( 'Can not find mkimage. Please install it and run this script again.' ) if os.path.exists(output_file): if ui.ask(text='Product keys file already exists.\n' 'Would you like to move it to "' + output_file + '.old" and make new file?'): os.replace(output_file, output_file + '.old') else: ui.print_message('Aborting key generation') return product_keys = ProductKeys(output_file) if product_id == ProductId.DA1469x_00: # DA1469x devices support only one signature generation algorithm - Ed25519. if elliptic_curve and elliptic_curve != EllipticCurve.EDWARDS25519.value: raise RuntimeError( 'Only {} curve could be used for DA1469x ({} passed). Execution ' 'aborted.'.format(EllipticCurve.EDWARDS25519.value, elliptic_curve)) elliptic_curve = EllipticCurve.EDWARDS25519.value elif not elliptic_curve: elliptic_curve = ui.select_item( text='Select elliptic curve used for asymmetric keys', item_list=[c.value for c in ALLOWED_ELLIPTIC_CURVES]) # Use Enum objects instead of string try: elliptic_curve = EllipticCurve(elliptic_curve) except ValueError: raise RuntimeError('Invalid Elliptic Curve passed. Execution aborted.') ui.print_message('Writing keys to {}'.format(output_file)) if SYMMETRIC_KEYS_NUM[product_id] and SYMMETRIC_KEYS_LEN[product_id]: for key in generate_symmetric_keys(SYMMETRIC_KEYS_NUM[product_id], SYMMETRIC_KEYS_LEN[product_id]): product_keys.add_symmetric_key(key=key) if ASYMMETRIC_KEYS_NUM[product_id] and elliptic_curve: for private, public in generate_asymmetric_keys( ASYMMETRIC_KEYS_NUM[product_id], elliptic_curve): product_keys.add_asymmetric_key(private=private, public=public, elliptic_curve=elliptic_curve) if SYMMETRIC_FW_DEC_KEYS_NUM[product_id] and SYMMETRIC_FW_DEC_KEYS_LEN[ product_id]: for key in generate_symmetric_keys( SYMMETRIC_FW_DEC_KEYS_NUM[product_id], SYMMETRIC_FW_DEC_KEYS_LEN[product_id]): product_keys.add_symmetric_fw_dec_key(key=key) if not product_keys.is_valid(product_id): raise RuntimeError('Generated keys are invalid - cannot save file.') product_keys.save()
def secure_keys_cfg(configuration_file=None, keys_file=None, prod_id=None, pub_key_idx=None, elliptic_curve=None, hash_method=None, key_revocations=None, min_version=None, sym_key_idx=None, nonce=None, enable_secure_boot=None): supported_prod_id = [ProductId.DA14683_00.value, ProductId.DA1469x_00.value] if configuration_file is None: configuration_file = DEFAULT_CONFIGURATION_FILE if keys_file is None: keys_file = DEFAULT_PRODUCT_KEYS_FILE if not os.path.exists(keys_file): if ui.ask(text='Would you like to create product keys file?'): if not prod_id: prod_id = ui.select_item(text='Select Product ID', item_list=supported_prod_id) if not prod_id: raise ExecutionCanceled() generate_keys(keys_file, elliptic_curve=elliptic_curve, prod_id=prod_id) else: return ui.print_message('Using product keys file: ' + os.path.normpath(keys_file)) security_config = SecurityConfig(configuration_file) product_keys = ProductKeys(keys_file) if os.path.exists(configuration_file) and security_config.is_valid(): configuration_str = prepare_configuration_str(security_config, product_keys) if not ui.ask(text='Would you like to change existing configuration?\n' + configuration_str, confirmation='Change', denial='Keep'): return # Could be passed or already selected if not prod_id: prod_id = ui.select_item(text='Select Product ID', item_list=supported_prod_id) if not prod_id: raise ExecutionCanceled() # Use Enum object instead of string try: product_id = ProductId(prod_id) except ValueError: raise RuntimeError('Invalid Product ID passed. Execution aborted.') if not product_keys.is_valid(product_id): raise RuntimeError('Invalid product keys file. Execution aborted.') if product_id == ProductId.DA1469x_00: if enable_secure_boot is None: enable_secure_boot = ui.ask(text='Would you like to enable secure boot in CS?') elif enable_secure_boot == 'enable': enable_secure_boot = True elif enable_secure_boot == 'disable': enable_secure_boot = False else: raise RuntimeError('No valid option for secure boot enabling in CS. Execution aborted.') # Set public key index if not passed if pub_key_idx is None: items = ['{0: <3} | {1: <15} | {2}'.format(str(index), key.elliptic_curve.value, key.private) for index, key in enumerate(product_keys.asymmetric_keys)] selected = ui.select_item(item_list=items, text='Select public key index') if not selected: raise ExecutionCanceled() pub_key_idx = next((e.strip() for e in selected.split('|'))) if product_id == ProductId.DA1469x_00: if not nonce and ui.ask(text='Would you like to add nonce?'): nonce = ui.ask_value(value_name='nonce', regex='[A-Fa-f0-9]{16}') if sym_key_idx is None: items = ['{0: <3} | {1}'.format(str(index), str(key)) for index, key in enumerate(product_keys.symmetric_fw_dec_keys)] selected = ui.select_item(item_list=items, text='Select index of the FW decryption symmetric key') if not selected: raise ExecutionCanceled() sym_key_idx = next((e.strip() for e in selected.split('|'))) if product_id == ProductId.DA14683_00: if not hash_method: hash_method = ui.select_item(item_list=[e.value for e in ALLOWED_HASH_METHODS], text='Select hash method', default=DEFAULT_HASH_METHOD_IDX) if not hash_method: raise ExecutionCanceled() hash_method = HashMethod(hash_method) if key_revocations is None and ui.ask(text='Would you like to add key revocations?'): asym_key_rev_list = list(range(4)) if product_id == ProductId.DA14683_00 else list(range(8)) sym_key_rev_list = list(range(8)) sym_fw_dec_rev_list = [] if product_id == ProductId.DA14683_00 else list(range(8)) # Remove indexes of keys which will be used in image's signature verification/decryption asym_key_rev_list = list(filter(lambda x: x != int(str(pub_key_idx), 0), asym_key_rev_list)) sym_fw_dec_rev_list = list(filter(lambda x: x != int(sym_key_idx), sym_fw_dec_rev_list)) # Ask for key revocation in each group key_revocations = dict() key_revocations[SecurityConfig.KEY_REV_PUB_TAG] = \ ask_key_rev(asym_key_rev_list, 'Select public key indexes which should be revoked.', product_keys.asymmetric_keys) if sym_fw_dec_rev_list: key_revocations[SecurityConfig.KEY_REV_SYM_FW_DEC_TAG] = \ ask_key_rev(sym_fw_dec_rev_list, 'Select FW decryption key indexes which should be ' 'revoked.', product_keys.symmetric_fw_dec_keys) key_revocations[SecurityConfig.KEY_REV_SYM_USER_DATA_TAG] = \ ask_key_rev(sym_key_rev_list, 'Select user data decryption key indexes which should be ' 'revoked.', product_keys.symmetric_keys) elif key_revocations is not None: # Don't remove public key and FW decryption key indexes which will be used for creating # image - leave all values which were given in command line call key_revocations = SecurityConfig.parse_revocation_list(key_revocations) security_config.product_id = product_id if product_id == ProductId.DA14683_00: if not min_version and ui.ask(text='Would you like to add minimal version?'): min_version = ui.ask_value(value_name='minimal version') if not min_version: raise ExecutionCanceled() security_config.security_pub_key_idx = pub_key_idx if key_revocations: security_config.adm_key_revocations = key_revocations else: # Keep empty dictionary of lists. This one from constructor could have been altered. security_config.adm_key_revocations = { SecurityConfig.KEY_REV_PUB_TAG : [], SecurityConfig.KEY_REV_SYM_USER_DATA_TAG : [], SecurityConfig.KEY_REV_SYM_FW_DEC_TAG : [] } # Set only required field for specified product - rest will stay as None if product_id == ProductId.DA14683_00: security_config.security_hash_method = hash_method security_config.adm_minimal_version = min_version elif product_id == ProductId.DA1469x_00: security_config.cs_enable_secure_boot = enable_secure_boot security_config.security_sym_key_idx = sym_key_idx security_config.security_nonce = nonce security_config.save() ui.print_message('Configuration saved to ' + configuration_file)
def secure_img_prog(image_file, secure_cfg_file, keys_file, cli_programmer, serial=None, header_config_path=None, flash_config_path=None): if image_file is None or not os.path.exists(image_file): raise RuntimeError("Application binary file not passed or not exist") qspi_config = handle_qspi_config(header_config_path, flash_config_path) if qspi_config.product_id != ProductId.DA1469x_00: raise RuntimeError( "This script could be run only for {} ({} selected)".format( ProductId.DA1469x_00.value, qspi_config.product_id)) if secure_cfg_file is None: secure_cfg_file = DEFAULT_SECURE_CFG_FILE if keys_file is None: keys_file = DEFAULT_PRODUCT_KEYS_FILE if not os.path.exists(secure_cfg_file) or not os.path.exists(keys_file): secure_keys_cfg(configuration_file=secure_cfg_file, keys_file=keys_file, prod_id=qspi_config.product_id) secure_cfg = SecurityConfig(secure_cfg_file) product_keys = ProductKeys(keys_file) if not secure_cfg.is_valid(): raise RuntimeError("Secure configuration is not valid.") if not product_keys.is_valid(secure_cfg.product_id): raise RuntimeError('Product keys file is not valid.') ui.print_message('Using product keys file: {}'.format( os.path.normpath(keys_file))) ui.print_message('Using secure configuration file: {}'.format( os.path.normpath(secure_cfg_file))) ui.print_title("Erasing product header area") cli_programmer.erase_qspi(FLASH_BASE_ADDRESS, PRODUCT_HEADER_SIZE, serial_port=serial) ui.print_title("Programming image") try: version_file = tempfile.mktemp() make_sw_version_file(version_file) try: output_image = tempfile.mktemp() asym_key = product_keys.asymmetric_keys[ secure_cfg.security_pub_key_idx] sym_key = product_keys.symmetric_fw_dec_keys[ secure_cfg.security_sym_key_idx] Mkimage().da1469x_secure(image_file, version_file, output_image, asym_key.private, secure_cfg.security_pub_key_idx, sym_key, secure_cfg.security_sym_key_idx, secure_cfg.security_nonce, secure_cfg.make_revocation_string()) cli_programmer.write_qspi(qspi_config.update_image_address, output_image, serial_port=serial, silent=False) finally: os.remove(output_image) finally: os.remove(version_file) ui.print_title("Programming product header") ui.print_message("Using configuration:\n" + qspi_config.get_config_info()) program_product_header(qspi_config, cli_programmer, serial)
def initial_flash(binary, bootloader=None, nobootloader=None, cfg=None, device_id=None, jlink_path=None, secure_config=None, keys=None, prod_id=None): config = ProgramQspiConfig() if prod_id is None: if config.is_valid(): prod_id = config.product_id else: raise RuntimeError( 'Product ID was not passed and the configuration file is not valid.' ) if not device_id: device_id = select_jlink_serial(jlink_path) cli = CliProgrammer(cfg_path=cfg, prod_id=prod_id) if not cfg: prepare_local_ini_file(cfg=cli.get_cfg(), prod_id=prod_id, device_id=device_id, jlink_path=jlink_path) if not os.path.exists(binary): raise RuntimeError('Binary file {} does not exist'.format(binary)) if secure_config is not None and not os.path.exists(secure_config): raise RuntimeError( 'Configuration file {} does not exist.\n' 'Run secure_keys_cfg.py to create one'.format(secure_config)) if secure_config and not keys: raise RuntimeError( 'Product keys file not passed - secure image cannot be created.') ui.print_message('Using SDK from {}'.format(SDK_ROOT)) ui.print_message('cli_programmer from {}'.format(cli.get_path())) ui.print_message('image file {}'.format(binary)) if prod_id == ProductId.DA1469x_00: ui.print_title('Erasing product header area') cli.erase_qspi(0, 4096) ui.print_title('Erasing partition table') cli.erase_qspi(NVMS_PARTITION_TABLE, 4096) program_qspi(binary, cli, prog_prod_header=True) elif prod_id == ProductId.DA14683_00 or prod_id == ProductId.DA14681_01: if not (bootloader or nobootloader): bootloader_bin = 'ble_suota_loader.bin' suota_build_config = \ prod_id.value + '-Release_' + ('OTP_Secure' if secure_config else 'QSPI') bootloader = os.path.join(SUOTA_LOADER_PATH, suota_build_config, bootloader_bin) ui.print_message('boot loader {}'.format(bootloader)) ui.print_title('Preparing image file {}'.format(IMAGE_FILE)) if secure_config: mkimage(binary, IMAGE_FILE, prod_id, VERSION_FILE, security_config=secure_config, product_keys=keys) else: mkimage(binary, IMAGE_FILE, prod_id) if not nobootloader: ui.print_title('Erasing bootloader area') cli.erase_qspi(0, 4096) ui.print_title('Erasing partition table') cli.erase_qspi(NVMS_PARTITION_TABLE, 4096) ui.print_title('Writing application image {}'.format(binary)) cli.write_qspi(NVMS_FW_EXEC_PART, binary) ui.print_title('Writing image header {}'.format(IMAGE_FILE)) cli.write_qspi(NVMS_IMAGE_HEADER_PART, IMAGE_FILE, 1024) os.remove(IMAGE_FILE) if not nobootloader: ui.print_title('Writing bootloader') if secure_config: cli.write_otp_exec(bootloader) else: program_qspi(bootloader, cli) if secure_config: if keys: prod_keys = ProductKeys(keys) if not prod_keys.is_valid(ProductId.DA14683_00): raise RuntimeError('Product key file is not valid.') ui.print_title("Writing symmetric keys") for index, key in enumerate(prod_keys.symmetric_keys): cli.write_key_sym(index, key) ui.print_title("Writing asymmetric keys") for index, key in enumerate(prod_keys.asymmetric_keys): cli.write_key_asym(index, key.public) # Write 0xAA to the product ready and the secure device field in OTP header cli.write_otp(PRODUCT_READY_ADDR, 1, 0xAA) cli.write_otp(SECURE_DEVICE_ADDR, 1, 0xAA) reboot_device(jlink_number=device_id, jlink_path=jlink_path) else: raise RuntimeError('Unsupported device selected ({}).'.format(prod_id))
def prepare_cli_programmer_ini(cfg=None, prod_id=None, device_id=None, port=None, log=None, target_reset_cmd=None, jlink_path=None): default_gdb_port = 2331 default_cfg = 'cli_programmer.ini' default_jlink_log = 'jlink.log' gdb_server_section = 'gdb server' trc_section = 'target reset' def get_gdb_server_path(jlink_path, prod_id, device_id, port, swoport, telnetport, log): gdb_server_path_template = Template( '$gdb -if swd -device $prod_id -endian little -speed 4000 -select usb=$device_id -port ' '$port -swoport $swoport -telnetport $telnetport -log $log') substitutes = { 'gdb': normpath(get_jlink_gdb(jlink_path)), 'prod_id': 'Cortex-M33' if prod_id == ProductId.DA1469x_00 else 'Cortex-M0', 'device_id': device_id, 'port': str(port), 'swoport': str(swoport), 'telnetport': str(telnetport), 'log': normpath(log) } gdb_server_path = gdb_server_path_template.substitute(substitutes) if not device_id: gdb_server_path = re.sub('-select usb=None *', '', gdb_server_path) return gdb_server_path if not device_id: device_id = select_jlink_serial(jlink_path) if not device_id: ui.print_message("No valid device selected.\nExecution aborted.\n") raise ExecutionCanceled() if cfg is None: cfg = default_cfg if not port: port = default_gdb_port if not log: log = default_jlink_log if not target_reset_cmd: target_reset_cmd = '' # Initialize cli_programmer.ini if doesn't exist CliProgrammer(cfg_path=cfg) config = ConfigParser() config.read(cfg) config.set( gdb_server_section, 'gdb_server_path', get_gdb_server_path(jlink_path=jlink_path, prod_id=prod_id, device_id=device_id, port=port, swoport=port + 1, telnetport=port + 2, log=log)) config.set(gdb_server_section, 'port', str(port)) trc_cmd = config.get(trc_section, 'target_reset_cmd') if len(trc_cmd) < 20: trc_cmd = target_reset_cmd if target_reset_cmd: if device_id: if '-selectemubysn' in trc_cmd: trc_cmd = re.sub('[ ]*-selectemubysn [0-9]*', ' -selectemubysn ' + device_id, trc_cmd) else: trc_cmd += ' -selectemubysn ' + device_id else: trc_cmd = re.sub("[ ]*-selectemubysn [0-9]*", "", trc_cmd) config.set(trc_section, 'target_reset_cmd', trc_cmd) with open(cfg, 'w+') as out_file: config.write(out_file)
def mkimage(binary_file, image_file=None, prod_id=None, version_file=None, secure_cfg_file=None, keys_file=None): if not os.path.isfile(MKIMAGE_BIN): raise RuntimeError('{} not found, please build it'.format(os.path.basename(MKIMAGE_BIN))) if not os.path.exists(binary_file): raise RuntimeError('Binary file {} does not exist'.format(binary_file)) if not version_file: version_file = DEFAULT_VERSION_FILE ui.print_message('Using SDK from {}'.format(SDK_ROOT)) version = DEFAULT_VERSION if os.path.isfile(version_file): with open(version_file, 'r') as f: try: version = SW_VERSION_PATTERN.findall(f.read())[0] except IndexError: os.rename(f.name, f.name + '.err') if not os.path.exists(version_file): if secure_cfg_file: raise RuntimeError('Invalid version file: {}'.format(version_file)) else: make_sw_version_file(version_file) if not image_file: file_name = binary_file.split('.bin')[0] image_file = '.'.join([file_name, version, 'img']) ui.print_message('No output image file specified, creating new file {}'.format(image_file)) if secure_cfg_file and not keys_file: raise RuntimeError('Product keys file not passed - secure image cannot be created.') if prod_id == ProductId.DA1469x_00: if secure_cfg_file: ui.print_message('Creating secure image for {}.'.format(prod_id)) secure_cfg = SecurityConfig(secure_cfg_file) keys = ProductKeys(keys_file) asym_key = keys.asymmetric_keys[secure_cfg.security_pub_key_idx] sym_key = keys.symmetric_fw_dec_keys[secure_cfg.security_sym_key_idx] if not secure_cfg.is_valid(): raise RuntimeError('Secure configuration is not valid.') if not keys.is_valid(secure_cfg.product_id): raise RuntimeError('Product key file is not valid.') return Mkimage().da1469x_secure(binary_file, version_file, image_file, key=asym_key.private, key_id=secure_cfg.security_pub_key_idx, sym_key=sym_key, sym_key_idx=secure_cfg.security_sym_key_idx, nonce=secure_cfg.security_nonce, rev_string=secure_cfg.make_revocation_string()) else: ui.print_message('Creating non secure image for DA1469x_00, {}.'.format(prod_id)) return Mkimage().da1469x(binary_file, version_file, image_file) elif prod_id == ProductId.DA14681_01 or prod_id == ProductId.DA14683_00: if secure_cfg_file: ui.print_message('Creating secure image for {}.'.format(prod_id)) secure_cfg = SecurityConfig(secure_cfg_file) keys = ProductKeys(keys_file) asym_key = keys.asymmetric_keys[secure_cfg.security_pub_key_idx] if not secure_cfg.is_valid(): raise RuntimeError('Secure configuration is not valid.') if not keys_file.is_valid(secure_cfg.product_id): raise RuntimeError('Product key file is not valid.') return Mkimage().secure(binary_file, version_file, image_file, ec=asym_key.elliptic_curve, hash_type=secure_cfg.security_hash_method, key=asym_key.private, key_id=secure_cfg.security_pub_key_idx, rev_string=secure_cfg.make_revocation_string(), min_ver=True if secure_cfg.adm_minimal_version else False, ver=secure_cfg.adm_minimal_version) else: return Mkimage().single(binary_file, version_file, image_file) else: raise RuntimeError('No supported revision selected ().'.format(prod_id))
def program_qspi_config(product_id=None, flash_config_path=None, header_config_path=None, flash_configuration=None, default_img_addr=False): config = ProgramQspiConfig(header_config_path) if not product_id: if config.product_id and not confirm_configuration_change(config): if header_config_path: config.save() return else: raise ExecutionCanceled() product_id = ui.select_item(text='Select Product ID', item_list=list(PRODUCT_IDS_DICT.keys())) if not product_id: ui.print_message( "No valid Product ID selected.\nExecution aborted.\n") raise ExecutionCanceled() config.product_id = PRODUCT_IDS_DICT[product_id] else: config.product_id = ProductId(product_id) if config.product_id == ProductId.DA1469x_00: if not flash_config_path: configurations = get_flash_configurations() else: ui.print_message( "\nUsing flash config from {}.\n".format(flash_config_path)) configurations = get_flash_configurations(flash_config_path) if not flash_configuration: flash_configuration = ui.select_item( item_list=list(configurations.keys()), text='Please select flash configuration') else: ui.print_message( "Selected flash: {}\n".format(flash_configuration)) if not flash_configuration: ui.print_message( "No valid flash config selected.\nExecution aborted.\n") raise ExecutionCanceled() try: configuration = configurations[flash_configuration] except: ui.print_message( "Unsupported flash config selected.\nExecution aborted.\n") raise ExecutionCanceled() config.flash_name = flash_configuration config.flash_size = int( configuration[ProgramQspiConfig.FLASH_SIZE_TAG], 16) config.flash_burstcmda_reg_value = \ int(configuration[ProgramQspiConfig.FLASH_BURSTCMDA_REG_VALUE_TAG], 16) config.flash_burstcmdb_reg_value = \ int(configuration[ProgramQspiConfig.FLASH_BURSTCMDB_REG_VALUE_TAG], 16) config.flash_write_config_command = config.parse_config_command( configuration[ProgramQspiConfig.FLASH_WRITE_CONFIG_COMMAND_TAG]) if default_img_addr: config.active_image_address = 0x2000 config.update_image_address = 0x2000 else: ui.print_title('Active FW Image Address\n') config.active_image_address = \ _setup_address(ProgramQspiConfig.ACTIVE_FW_IMAGE_ADDRESS_TAG, 'Insert Active FW image address (hex)') ui.print_title('Update FW Image Address\n') config.update_image_address = \ _setup_address(ProgramQspiConfig.UPDATE_FW_IMAGE_ADDRESS_TAG, 'Insert Update FW image address (hex)') config.save()