Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

222 lines
8.1 KiB

# Copyright (c) 2023 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
import logging
from pathlib import Path
import pytest
from twister_harness import DeviceAdapter, MCUmgr, Shell
from twister_harness.helpers.utils import find_in_config, match_lines, match_no_lines
from utils import check_with_mcumgr_command, check_with_shell_command
from west_sign_wrapper import west_sign_with_imgtool
logger = logging.getLogger(__name__)
# This string is used to verify that the device is running the application
WELCOME_STRING = "smp_sample: build time:"
def create_signed_image(build_dir: Path, app_build_dir: Path, version: str) -> Path:
image_to_test = Path(build_dir) / 'test_{}.signed.bin'.format(
version.replace('.', '_').replace('+', '_'))
origin_key_file = find_in_config(
Path(build_dir) / 'mcuboot' / 'zephyr' / '.config',
'CONFIG_BOOT_SIGNATURE_KEY_FILE'
)
west_sign_with_imgtool(
build_dir=Path(app_build_dir),
output_bin=image_to_test,
key_file=Path(origin_key_file),
version=version
)
assert image_to_test.is_file()
return image_to_test
def get_upgrade_string_to_verify(build_dir: Path) -> str:
sysbuild_config = Path(build_dir) / 'zephyr' / '.config'
if find_in_config(sysbuild_config, 'SB_CONFIG_MCUBOOT_MODE_SWAP_USING_OFFSET'):
return 'Starting swap using offset algorithm'
return 'Starting swap using move algorithm'
def clear_buffer(dut: DeviceAdapter) -> None:
disconnect = False
if not dut.is_device_connected():
dut.connect()
disconnect = True
dut.clear_buffer()
if disconnect:
dut.disconnect()
def run_upgrade_with_confirm(dut: DeviceAdapter, shell: Shell, mcumgr: MCUmgr):
"""
Verify that the application can be updated
1) Device flashed with MCUboot and an application that contains SMP server
2) Prepare an update of an application containing the SMP server
3) Upload the application update to slot 1 using mcumgr
4) Flag the application update in slot 1 as 'pending' by using mcumgr 'test'
5) Restart the device, verify that swapping process is initiated
6) Verify that the updated application is booted
7) Confirm the image using mcumgr
8) Restart the device, and verify that the new application is still booted
"""
logger.info('Prepare upgrade image')
new_version = '0.0.2+0'
image_to_test = create_signed_image(dut.device_config.build_dir,
dut.device_config.app_build_dir, new_version)
logger.info('Upload image with mcumgr')
dut.disconnect()
mcumgr.image_upload(image_to_test)
logger.info('Test uploaded APP image')
second_hash = mcumgr.get_hash_to_test()
mcumgr.image_test(second_hash)
clear_buffer(dut)
mcumgr.reset_device()
dut.connect()
output = dut.readlines_until(WELCOME_STRING)
upgrade_string_to_verify = get_upgrade_string_to_verify(dut.device_config.build_dir)
match_lines(output, [
'Swap type: test',
upgrade_string_to_verify
])
logger.info('Verify new APP is booted')
check_with_shell_command(shell, new_version, swap_type='test')
dut.disconnect()
check_with_mcumgr_command(mcumgr, new_version)
logger.info('Confirm the image')
mcumgr.image_confirm(second_hash)
mcumgr.reset_device()
dut.connect()
output = dut.readlines_until(WELCOME_STRING)
match_no_lines(output, [
upgrade_string_to_verify
])
logger.info('Verify new APP is still booted')
check_with_shell_command(shell, new_version)
def test_upgrade_with_confirm(mcumgr: MCUmgr, dut: DeviceAdapter, shell: Shell):
"""Verify that the application can be updated over serial"""
run_upgrade_with_confirm(dut, shell, mcumgr)
def test_upgrade_with_revert(dut: DeviceAdapter, shell: Shell, mcumgr: MCUmgr):
"""
Verify that MCUboot will roll back an image that is not confirmed
1) Device flashed with MCUboot and an application that contains SMP server
2) Prepare an update of an application containing the SMP server
3) Upload the application update to slot 1 using mcumgr
4) Flag the application update in slot 1 as 'pending' by using mcumgr 'test'
5) Restart the device, verify that swapping process is initiated
6) Verify that the updated application is booted
7) Reset the device without confirming the image
8) Verify that MCUboot reverts update
"""
origin_version = find_in_config(
Path(dut.device_config.app_build_dir) / 'zephyr' / '.config',
'CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION'
)
logger.info('Prepare upgrade image')
new_version = '0.0.3+0'
image_to_test = create_signed_image(dut.device_config.build_dir,
dut.device_config.app_build_dir, new_version)
logger.info('Upload image with mcumgr')
dut.disconnect()
mcumgr.image_upload(image_to_test)
logger.info('Test uploaded APP image')
second_hash = mcumgr.get_hash_to_test()
mcumgr.image_test(second_hash)
clear_buffer(dut)
mcumgr.reset_device()
dut.connect()
output = dut.readlines_until(WELCOME_STRING)
upgrade_string_to_verify = get_upgrade_string_to_verify(dut.device_config.build_dir)
match_lines(output, [
'Swap type: test',
upgrade_string_to_verify
])
logger.info('Verify new APP is booted')
check_with_shell_command(shell, new_version, swap_type='test')
dut.disconnect()
check_with_mcumgr_command(mcumgr, new_version)
logger.info('Revert images')
mcumgr.reset_device()
dut.connect()
output = dut.readlines_until(WELCOME_STRING)
match_lines(output, [
'Swap type: revert',
upgrade_string_to_verify
])
logger.info('Verify that MCUboot reverts update')
check_with_shell_command(shell, origin_version)
@pytest.mark.parametrize(
'key_file', [None, 'root-ec-p256.pem'],
ids=['no_key', 'invalid_key']
)
def test_upgrade_signature(dut: DeviceAdapter, shell: Shell, mcumgr: MCUmgr, key_file):
"""
Verify that the application is not updated when app is not signed or signed with invalid key
1) Device flashed with MCUboot and an application that contains SMP server
2) Prepare an update of an application containing the SMP server that has
been signed:
a) without any key
b) with a different key than MCUboot was compiled with
3) Upload the application update to slot 1 using mcumgr
4) Flag the application update in slot 1 as 'pending' by using mcumgr 'test'
5) Restart the device, verify that swap is not started
"""
if key_file:
origin_key_file = find_in_config(
Path(dut.device_config.build_dir) / 'mcuboot' / 'zephyr' / '.config',
'CONFIG_BOOT_SIGNATURE_KEY_FILE'
).strip('"\'')
key_file = Path(origin_key_file).parent / key_file
assert key_file.is_file()
assert not key_file.samefile(origin_key_file)
image_to_test = Path(dut.device_config.build_dir) / 'test_invalid_key.bin'
logger.info('Sign second image with an invalid key')
else:
image_to_test = Path(dut.device_config.build_dir) / 'test_no_key.bin'
logger.info('Sign second imagewith no key')
west_sign_with_imgtool(
build_dir=Path(dut.device_config.app_build_dir),
output_bin=image_to_test,
key_file=key_file,
version='0.0.3+4' # must differ from the origin version, if not then hash is not updated
)
assert image_to_test.is_file()
logger.info('Upload image with mcumgr')
dut.disconnect()
mcumgr.image_upload(image_to_test)
logger.info('Test uploaded APP image')
second_hash = mcumgr.get_hash_to_test()
mcumgr.image_test(second_hash)
logger.info('Verify that swap is not started')
clear_buffer(dut)
mcumgr.reset_device()
dut.connect()
output = dut.readlines_until(WELCOME_STRING)
upgrade_string_to_verify = get_upgrade_string_to_verify(dut.device_config.build_dir)
match_no_lines(output, [upgrade_string_to_verify])
match_lines(output, ['Image in the secondary slot is not valid'])