Example #1
0
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
Example #2
0
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
Example #3
0
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)
Example #4
0
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()
Example #5
0
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
Example #6
0
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
Example #7
0
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.")
Example #8
0
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)
Example #9
0
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))
Example #10
0
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()
Example #11
0
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)
Example #12
0
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)
Example #13
0
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))
Example #14
0
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)
Example #15
0
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))
Example #16
0
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()