diff --git a/suit_generator/cmd_image.py b/suit_generator/cmd_image.py index 0bee664a..c01f6335 100644 --- a/suit_generator/cmd_image.py +++ b/suit_generator/cmd_image.py @@ -43,40 +43,11 @@ def add_arguments(parser): "--storage-output-directory", required=True, help="Output hex file with SUIT storage contents" ) cmd_image_boot.add_argument( - "--update-candidate-info-address", + "--storage-address", required=False, type=lambda x: int(x, 0), - default=ImageCreator.default_update_candidate_info_address, - help=f"Address of SUIT storage update candidate info. " - f"Default: 0x{ImageCreator.default_update_candidate_info_address:08X}", - ) - cmd_image_boot.add_argument( - "--envelope-address", - required=False, - type=lambda x: int(x, 0), - default=ImageCreator.default_envelope_address, - help=f"Address of installed envelope in SUIT storage. Default: 0x{ImageCreator.default_envelope_address:08X}", - ) - cmd_image_boot.add_argument( - "--envelope-slot-size", - required=False, - type=lambda x: int(x, 0), - default=ImageCreator.default_envelope_slot_size, - help=f"Envelope slot size in SUIT storage. Default: 0x{ImageCreator.default_envelope_slot_size:08X}", - ) - cmd_image_boot.add_argument( - "--envelope-slot-count", - required=False, - type=lambda x: int(x, 0), - default=ImageCreator.default_envelope_slot_count, - help=f"Max number of envelope slots in SUIT storage. Default: {ImageCreator.default_envelope_slot_count}", - ) - cmd_image_boot.add_argument( - "--dfu-max-caches", - required=False, - type=int, - default=ImageCreator.default_dfu_max_caches, - help=f"Max number of DFU caches. Default: {ImageCreator.default_dfu_max_caches}", + default=ImageCreator.default_storage_address, + help=f"Address of SUIT storage. Default: 0x{ImageCreator.default_storage_address:08X}", ) cmd_image_update.add_argument("--input-file", required=True, help="Input SUIT file; an envelope") @@ -137,51 +108,14 @@ class ManifestDomain(Enum): class EnvelopeStorage: - """Class generating SUIT storage binary in legacy format.""" + """Base class for generating SUIT storage binary.""" ENVELOPE_SLOT_VERSION = 1 ENVELOPE_SLOT_VERSION_KEY = 0 ENVELOPE_SLOT_CLASS_ID_OFFSET_KEY = 1 ENVELOPE_SLOT_ENVELOPE_BSTR_KEY = 2 - _LAYOUT = [ - { - "role": ManifestRole.APP_ROOT, - "offset": 2048 * 0, - "size": 2048, - "domain": ManifestDomain.APPLICATION, - }, - { - "role": ManifestRole.APP_LOCAL_1, - "offset": 2048 * 1, - "size": 2048, - "domain": ManifestDomain.APPLICATION, - }, - { - "role": ManifestRole.RAD_LOCAL_1, - "offset": 2048 * 2, - "size": 2048, - "domain": ManifestDomain.RADIO, - }, - { - "role": ManifestRole.SEC_TOP, - "offset": 2048 * 3, - "size": 2048, - "domain": ManifestDomain.SECURE, - }, - { - "role": ManifestRole.SEC_SDFW, - "offset": 2048 * 4, - "size": 2048, - "domain": ManifestDomain.SECURE, - }, - { - "role": ManifestRole.SEC_SYSCTRL, - "offset": 2048 * 5, - "size": 2048, - "domain": ManifestDomain.SECURE, - }, - ] + _LAYOUT = [] # Default manifest role assignments _CLASS_ROLE_ASSIGNMENTS = [ @@ -414,10 +348,8 @@ class EnvelopeStorageNrf54h20(EnvelopeStorage): class ImageCreator: """Helper class for extracting data from SUIT envelope and creating hex files.""" - default_update_candidate_info_address = 0x0E1E9340 - default_envelope_address = 0x0E1E7000 - default_envelope_slot_size = 2048 - default_envelope_slot_count = 8 + default_update_candidate_info_address = 0x0E1ED340 + default_storage_address = 0x0E1EB000 default_dfu_partition_address = 0x0E155000 default_dfu_max_caches = 6 @@ -435,21 +367,6 @@ def _prepare_suit_storage_struct_format(dfu_max_caches: int) -> str: # (void*, size_t) for each cache return "<" + "IIII" + dfu_max_caches * "II" - @staticmethod - def _prepare_update_candidate_info_for_boot(dfu_max_caches: int) -> bytes: - uci = struct.Struct(ImageCreator._prepare_suit_storage_struct_format(dfu_max_caches)) - - all_cache_values = dfu_max_caches * [0, 0] # address, size - struct_values = [ - ImageCreator.UPDATE_MAGIC_VALUE_AVAILABLE, # Update candidate info magic - 0, # Nb of memory regions - 0, # SUIT envelope address - 0, # SUIT envelope size - *all_cache_values, # Values for all the caches - ] - - return uci.pack(*struct_values) - @staticmethod def _prepare_update_candidate_info_for_update( dfu_partition_address: int, candidate_size: int, dfu_max_caches: int @@ -472,69 +389,27 @@ def _create_single_domain_storage_file_for_boot( storage: EnvelopeStorage, domain: ManifestDomain, dir_name: str, - additional_hex, ) -> None: combined_hex = IntelHex() - if additional_hex is not None: - combined_hex = IntelHex(additional_hex) envelopes_hex = storage.as_intelhex(domain) if envelopes_hex is not None: combined_hex.merge(envelopes_hex) - combined_hex.write_hex_file(dir_name + "/storage_" + domain.name.lower() + ".hex") - - def _create_suit_storage_file_for_boot_legacy( - envelopes: list[SuitEnvelope], - update_candidate_info_address: int, - installed_envelope_address: int, - envelope_slot_size: int, - envelope_slot_count: int, - dir_name: str, - dfu_max_caches: int, - ) -> None: - # Update candidate info - # In the boot path it is used to inform no update candidate is pending. - uci_hex = IntelHex() - uci_hex.frombytes( - ImageCreator._prepare_update_candidate_info_for_boot(dfu_max_caches), update_candidate_info_address - ) - - combined_hex = IntelHex(uci_hex) - - storage = EnvelopeStorageNrf54h20(installed_envelope_address) - for envelope in envelopes: - storage.add_envelope(envelope) - combined_hex.merge(storage.as_intelhex()) - - combined_hex.write_hex_file(dir_name + "/storage.hex") + combined_hex.write_hex_file(dir_name + "/suit_installed_envelopes_" + domain.name.lower() + "_merged.hex") @staticmethod def _create_suit_storage_files_for_boot( envelopes: list[SuitEnvelope], - update_candidate_info_address: int, - installed_envelope_address: int, - envelope_slot_size: int, - envelope_slot_count: int, + storage_address: int, dir_name: str, - dfu_max_caches: int, ) -> None: - # Update candidate info - # In the boot path it is used to inform no update candidate is pending. - uci_hex = IntelHex() - uci_hex.frombytes( - ImageCreator._prepare_update_candidate_info_for_boot(dfu_max_caches), update_candidate_info_address - ) - - storage = EnvelopeStorageNrf54h20(installed_envelope_address) + storage = EnvelopeStorageNrf54h20(storage_address) for envelope in envelopes: storage.add_envelope(envelope) for domain in ManifestDomain: - additional_hex = None - if domain == ManifestDomain.APPLICATION: - additional_hex = uci_hex - ImageCreator._create_single_domain_storage_file_for_boot(storage, domain, dir_name, additional_hex) + ImageCreator._create_single_domain_storage_file_for_boot(storage, domain, dir_name,) @staticmethod def _create_suit_storage_file_for_update( @@ -563,21 +438,13 @@ def _create_dfu_partition_hex_file(input_file: str, dfu_partition_output_file: s def create_files_for_boot( input_files: list[str], storage_output_directory: str, - update_candidate_info_address: int, - envelope_address: int, - envelope_slot_size: int, - envelope_slot_count: int, - dfu_max_caches: int, + storage_address: int, ) -> None: """Create storage and payload hex files allowing boot execution path. :param input_file: file path to SUIT envelope :param storage_output_directory: directory path to store hex files with SUIT storage contents - :param update_candidate_info_address: address of SUIT storage update candidate info - :param envelope_address: address of installed envelope in SUIT storage - :param envelope_slot_size: number of bytes, reserved to store a single envelope, - :param envelope_slot_count: number of envelope slots in SUIT storage, - :param dfu_max_caches: maximum number of caches, allowed to be passed inside update candidate info, + :param storage_address: address of SUIT storage """ try: envelopes = [] @@ -588,23 +455,10 @@ def create_files_for_boot( envelope.sever() envelopes.append(envelope) - ImageCreator._create_suit_storage_file_for_boot_legacy( - envelopes, - update_candidate_info_address, - envelope_address, - envelope_slot_size, - envelope_slot_count, - storage_output_directory, - dfu_max_caches, - ) ImageCreator._create_suit_storage_files_for_boot( envelopes, - update_candidate_info_address, - envelope_address, - envelope_slot_size, - envelope_slot_count, + storage_address, storage_output_directory, - dfu_max_caches, ) except FileNotFoundError as error: raise GeneratorError(error) @@ -647,22 +501,19 @@ def main(**kwargs) -> None: :Keyword Arguments: * **image** - subcommand to be executed * **input_file** - file path to SUIT envelope - * **storage_output_file** - file path to hex file with SUIT storage contents - for "update" command * **storage_output_directory** - directory path to store hex files with storage contents - for "boot" command - * **update_candidate_info_address** - address of SUIT storage update candidate info - * **envelope_address** - address of installed envelope in SUIT storage - * **dfu_partition_output_file** - file path to hex file with DFU partition contents (the SUIT envelope) - * **dfu_partition_address** - address of partition where DFU update candidate is stored + * **storage_address** - address of SUIT storage - for "boot" command + * **storage_output_file** - file path to hex file with SUIT storage contents - for "update" command + * **update_candidate_info_address** - address of SUIT storage update candidate info - for "update" command + * **dfu_partition_output_file** - file path to hex file with DFU partition contents (the SUIT envelope) - for "update" command + * **dfu_partition_address** - address of partition where DFU update candidate is stored - for "update" command + * **dfu_max_caches** - maximum number of caches, allowed to be passed inside update candidate info - for "update" command """ if kwargs["image"] == ImageCreator.IMAGE_CMD_BOOT: ImageCreator.create_files_for_boot( kwargs["input_file"], kwargs["storage_output_directory"], - kwargs["update_candidate_info_address"], - kwargs["envelope_address"], - kwargs["envelope_slot_size"], - kwargs["envelope_slot_count"], - kwargs["dfu_max_caches"], + kwargs["storage_address"], ) elif kwargs["image"] == ImageCreator.IMAGE_CMD_UPDATE: ImageCreator.create_files_for_update( diff --git a/suit_generator/cmd_mpi.py b/suit_generator/cmd_mpi.py index f3e4c0a5..d51610ee 100644 --- a/suit_generator/cmd_mpi.py +++ b/suit_generator/cmd_mpi.py @@ -90,7 +90,7 @@ def add_arguments(parser): class MpiGenerator: - """Class geenrating SUIT Manifest Provisioning Information.""" + """Class generating SUIT Manifest Provisioning Information.""" BYTE_ORDER = "little" diff --git a/tests/test_cmd_image.py b/tests/test_cmd_image.py index 45664f94..51f3abca 100644 --- a/tests/test_cmd_image.py +++ b/tests/test_cmd_image.py @@ -6,7 +6,7 @@ import pytest -from suit_generator.cmd_image import ImageCreator, EnvelopeStorage +from suit_generator.cmd_image import ImageCreator, EnvelopeStorageNrf54h20 from suit_generator.cmd_image import main as cmd_image_main from suit_generator.exceptions import GeneratorError, SUITError @@ -61,14 +61,10 @@ malformed_envelope_input = b"\x00" expected_boot_storage = ( - # Empty update candidate info (0x0E1E9340) - ":020000040E1ECE\n" - ":10934000AA55AA550000000000000000000000001F\n" - ":10935000000000000000000000000000000000000D\n" - ":1093600000000000000000000000000000000000FD\n" - ":1093700000000000000000000000000000000000ED\n" + # Empty update candidate info (0x0E1E9340 - 0x0E1E9380) # Uninitialized NVV area (0x0E1E9380 - 0x0E1E9400) # Empty root manifest slot (0x0E1E9400) + ":020000040E1ECE\n" ":10940000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C\n" ":10941000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5C\n" ":10942000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF4C\n" @@ -596,17 +592,6 @@ def test_struct_format(nb_of_caches): assert ImageCreator._prepare_suit_storage_struct_format(nb_of_caches) == format -@pytest.mark.parametrize("nb_of_caches", range(MAX_CACHE_COUNT + 1)) -def test_update_candidate_info_for_boot(nb_of_caches): - suit_storage_bytes = b"\xAA\x55\xAA\x55\x00\x00\x00\x00" - envelope_address_size_bytes = b"\x00\x00\x00\x00\x00\x00\x00\x00" - caches_bytes = b"\x00\x00\x00\x00\x00\x00\x00\x00" * nb_of_caches - - expected_bytes = suit_storage_bytes + envelope_address_size_bytes + caches_bytes - - assert ImageCreator._prepare_update_candidate_info_for_boot(nb_of_caches) == expected_bytes - - def test_update_candidate_info_verify_class_id_offset(): from suit_generator.envelope import SuitEnvelope from suit_generator.suit.envelope import SuitEnvelopeTagged @@ -617,14 +602,14 @@ def test_update_candidate_info_verify_class_id_offset(): envelope._envelope = SuitEnvelopeTagged.from_cbor(signed_envelope_input).to_obj() # Generate storage envelope slot for the envelope - storage = EnvelopeStorage(0) + storage = EnvelopeStorageNrf54h20(0) storage.add_envelope(envelope) (envelope_role, envelope_cbor) = storage._envelopes.popitem() # Extract the class ID, based on the offset and minified envelope storage_dict = cbor_loads(envelope_cbor) - offset = storage_dict[EnvelopeStorage.ENVELOPE_SLOT_CLASS_ID_OFFSET_KEY] - envelope_bstr = storage_dict[EnvelopeStorage.ENVELOPE_SLOT_ENVELOPE_BSTR_KEY] + offset = storage_dict[EnvelopeStorageNrf54h20.ENVELOPE_SLOT_CLASS_ID_OFFSET_KEY] + envelope_bstr = storage_dict[EnvelopeStorageNrf54h20.ENVELOPE_SLOT_ENVELOPE_BSTR_KEY] # RFC4122 uuid5(nordic_vid, 'nRF54H20_sample_app') exp_class_id = b"\x08\xc1\xb5\x99\x55\xe8\x5f\xbc\x9e\x76\x7b\xc2\x9c\xe1\xb0\x4d" @@ -657,7 +642,7 @@ def test_unsupported_image_subcommand(): input_file="", storage_output_file="", update_candidate_info_address=0, - envelope_address=0, + storage_address=0, dfu_partition_output_file="", dfu_partition_address=0, dfu_max_caches=0, @@ -671,7 +656,7 @@ def test_boot_subcommand_nonexisting_input_file(): input_file=["nonexisting"], storage_output_directory="", update_candidate_info_address=0, - envelope_address=0, + storage_address=0, envelope_slot_size=2048, envelope_slot_count=8, dfu_partition_output_file="", @@ -690,7 +675,7 @@ def test_boot_subcommand_manifest_without_component_id(mocker): input_file=["some_input"], storage_output_directory="some_output", update_candidate_info_address=0x0E1EEC00, - envelope_address=0x0E1EED80, + storage_address=0x0E1EED80, envelope_slot_size=2048, envelope_slot_count=8, dfu_partition_output_file="", @@ -708,7 +693,7 @@ def test_boot_subcommand_success(mocker): input_file=["some_input"], storage_output_directory="some_output", update_candidate_info_address=0x0E1E9340, - envelope_address=0x0E1E7000, + storage_address=0x0E1E7000, envelope_slot_size=2048, envelope_slot_count=1, dfu_partition_output_file="", @@ -727,7 +712,7 @@ def test_update_subcommand_nonexisting_input_file(): input_file="nonexisting", storage_output_file="", update_candidate_info_address=0, - envelope_address=0, + storage_address=0, dfu_partition_output_file="", dfu_partition_address=0, dfu_max_caches=0, @@ -748,7 +733,7 @@ def test_update_subcommand_success(mocker): input_file="some_input", storage_output_file="some_storage_output", update_candidate_info_address=0x0E1EEC00, - envelope_address=0x0E1EED80, + storage_address=0x0E1EED80, dfu_partition_output_file="some_dfu_partition_output", dfu_partition_address=0x0E100000, dfu_max_caches=4, @@ -770,7 +755,7 @@ def test_malformed_envelope(mocker): input_file=["some_input"], storage_output_directory="some_output", update_candidate_info_address=0x0E1FE000, - envelope_address=0x0E1FF000, + storage_address=0x0E1FF000, envelope_slot_size=2048, envelope_slot_count=8, dfu_partition_output_file="", @@ -803,7 +788,7 @@ def bin2hex_mock(*args, **kwargs): input_file="some_input", storage_output_file="some_storage_output", update_candidate_info_address=0x0E1EEC00, - envelope_address=0x0E1EED80, + storage_address=0x0E1EED80, dfu_partition_output_file="some_dfu_partition_output", dfu_partition_address=0x0E100000, dfu_max_caches=4,