Skip to content

Commit

Permalink
cmd_image: Add support for manifest hierarchy
Browse files Browse the repository at this point in the history
Generate a slot for each envelope in SUIT storage.

Signed-off-by: Tomasz Chyrowicz <[email protected]>
  • Loading branch information
tomchy committed Oct 27, 2023
1 parent bfa3b11 commit 42489ab
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 20 deletions.
9 changes: 6 additions & 3 deletions ncs/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ def get_absolute_address(node):
parent_parser.add_argument(
"--core", action="append", required=True, help="Configuration of sample name:location of binaries:location of edt"
)
parent_parser.add_argument("--output-envelope", required=True, help="Location of output envelope.")
parent_parser.add_argument("--zephyr-base", required=True, help="Location of zephyr directory.")

parser = ArgumentParser(add_help=False)
Expand All @@ -76,27 +75,31 @@ def get_absolute_address(node):
cmd_template_arg_parser.add_argument("--template-suit", required=True, help="Input SUIT jinja2 template.")
cmd_template_arg_parser.add_argument("--output-suit", required=True, help="Output SUIT configuration.")

cmd_storage_arg_parser.add_argument(
"--input-envelope", required=True, action="append", help="Location of input envelope(s)."
)
cmd_storage_arg_parser.add_argument("--storage-output-file", required=True, help="Input binary SUIT envelope.")

arguments = parser.parse_args()

sys.path.insert(0, os.path.join(arguments.zephyr_base, "scripts", "dts", "python-devicetree", "src"))

configuration = read_configurations(arguments.core)
configuration["output_envelope"] = arguments.output_envelope

if arguments.command == TEMPLATE_CMD:
configuration["version"] = arguments.version
configuration["output_envelope"] = arguments.output_suit
output_suit_content = render_template(arguments.template_suit, configuration)
with open(arguments.output_suit, "w") as output_file:
output_file.write(output_suit_content)

elif arguments.command == STORAGE_CMD:
# fixme: envelope_address, update_candidate_info_address and dfu_max_caches shall be extracted from DTS
ImageCreator.create_files_for_boot(
input_file=arguments.output_envelope,
input_files=arguments.input_envelope,
storage_output_file=arguments.storage_output_file,
envelope_address=ImageCreator.default_envelope_address,
envelope_slot_size=ImageCreator.default_envelope_slot_size,
update_candidate_info_address=ImageCreator.default_update_candidate_info_address,
dfu_max_caches=ImageCreator.default_dfu_max_caches,
)
57 changes: 45 additions & 12 deletions suit_generator/cmd_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
#
"""Use SUIT envelope to generate hex files allowing boot/update execution path."""

from __future__ import annotations

import os
import struct

Expand Down Expand Up @@ -34,7 +36,7 @@ def add_arguments(parser):
ImageCreator.IMAGE_CMD_UPDATE, help="Generate .hex files for update execution path"
)

cmd_image_boot.add_argument("--input-file", required=True, help="Input SUIT file; an envelope")
cmd_image_boot.add_argument("--input-file", required=True, action="append", help="Input SUIT file; an envelope")
cmd_image_boot.add_argument(
"--storage-output-file", required=True, help="Output hex file with SUIT storage contents"
)
Expand All @@ -53,6 +55,13 @@ def add_arguments(parser):
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(
"--dfu-max-caches",
required=False,
Expand Down Expand Up @@ -103,6 +112,7 @@ class ImageCreator:

default_update_candidate_info_address = 0x0E1EEC00
default_envelope_address = 0x0E1EED80
default_envelope_slot_size = 2048
default_dfu_partition_address = 0x0E100000
default_dfu_max_caches = 4

Expand Down Expand Up @@ -181,9 +191,10 @@ def _prepare_envelope_slot_binary(envelope: SuitEnvelope) -> bytes:

@staticmethod
def _create_suit_storage_file_for_boot(
envelope: SuitEnvelope,
envelopes: list[SuitEnvelope],
update_candidate_info_address: int,
installed_envelope_address: int,
envelope_slot_size: int,
file_name: str,
dfu_max_caches: int,
) -> None:
Expand All @@ -193,13 +204,25 @@ def _create_suit_storage_file_for_boot(
ImageCreator._prepare_update_candidate_info_for_boot(dfu_max_caches), update_candidate_info_address
)

# Installed envelope
envelope_hex = IntelHex()
envelope_hex.frombytes(ImageCreator._prepare_envelope_slot_binary(envelope), installed_envelope_address)

# The suit storage file for boot path combines update candidate info and installed envelope
combined_hex = IntelHex(uci_hex)
combined_hex.merge(envelope_hex)

# Installed envelopes
envelope_address = installed_envelope_address

for envelope in envelopes:
envelope_bytes = ImageCreator._prepare_envelope_slot_binary(envelope)
if len(envelope_bytes) > envelope_slot_size:
raise GeneratorError(
f"Input envelope ({envelope}) exceeds slot size ({len(envelope_bytes)} > {envelope_slot_size})."
)

envelope_hex = IntelHex()
envelope_hex.frombytes(envelope_bytes, envelope_address)

combined_hex.merge(envelope_hex)
envelope_address += envelope_slot_size

combined_hex.write_hex_file(file_name)

@staticmethod
Expand Down Expand Up @@ -227,10 +250,11 @@ def _create_dfu_partition_hex_file(input_file: str, dfu_partition_output_file: s

@staticmethod
def create_files_for_boot(
input_file: str,
input_files: list[str],
storage_output_file: str,
update_candidate_info_address: int,
envelope_address: int,
envelope_slot_size: int,
dfu_max_caches: int,
) -> None:
"""Create storage and payload hex files allowing boot execution path.
Expand All @@ -241,13 +265,21 @@ def create_files_for_boot(
:param envelope_address: address of installed envelope in SUIT storage
"""
try:
envelope = SuitEnvelope()
envelope.load(input_file, "suit")
envelopes = []
for input_file in input_files:
envelope = SuitEnvelope()
envelope.load(input_file, "suit")

envelope.sever()
envelope.sever()
envelopes.append(envelope)

ImageCreator._create_suit_storage_file_for_boot(
envelope, update_candidate_info_address, envelope_address, storage_output_file, dfu_max_caches
envelopes,
update_candidate_info_address,
envelope_address,
envelope_slot_size,
storage_output_file,
dfu_max_caches,
)
except FileNotFoundError as error:
raise GeneratorError(error)
Expand Down Expand Up @@ -302,6 +334,7 @@ def main(**kwargs) -> None:
kwargs["storage_output_file"],
kwargs["update_candidate_info_address"],
kwargs["envelope_address"],
kwargs["envelope_slot_size"],
kwargs["dfu_max_caches"],
)
elif kwargs["image"] == ImageCreator.IMAGE_CMD_UPDATE:
Expand Down
8 changes: 7 additions & 1 deletion suit_generator/envelope.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ def load(self, file_name: str, input_type: str = "AUTO") -> None:

def sever(self) -> None:
"""Get rid of severable elements."""
severable = ["suit-payload-fetch", "suit-install", "suit-text", "suit-integrated-payloads"]
severable = [
"suit-payload-fetch",
"suit-install",
"suit-text",
"suit-integrated-payloads",
"suit-integrated-dependencies",
]
[
self._envelope["SUIT_Envelope_Tagged"].pop(k, None)
for k in list(self._envelope["SUIT_Envelope_Tagged"].keys())
Expand Down
12 changes: 8 additions & 4 deletions tests/test_cmd_image.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,11 @@ def test_boot_subcommand_nonexisting_input_file():
with pytest.raises(GeneratorError):
cmd_image_main(
image="boot",
input_file="nonexisting",
input_file=["nonexisting"],
storage_output_file="",
update_candidate_info_address=0,
envelope_address=0,
envelope_slot_size=2048,
dfu_partition_output_file="",
dfu_partition_address=0,
dfu_max_caches=0,
Expand All @@ -228,10 +229,11 @@ def test_boot_subcommand_manifest_without_component_id(mocker):
with pytest.raises(GeneratorError):
cmd_image_main(
image="boot",
input_file="some_input",
input_file=["some_input"],
storage_output_file="some_output.hex",
update_candidate_info_address=0x0E1EEC00,
envelope_address=0x0E1EED80,
envelope_slot_size=2048,
dfu_partition_output_file="",
dfu_partition_address=0,
dfu_max_caches=4,
Expand All @@ -244,10 +246,11 @@ def test_boot_subcommand_success(mocker):

cmd_image_main(
image="boot",
input_file="some_input",
input_file=["some_input"],
storage_output_file="some_output.hex",
update_candidate_info_address=0x0E1EEC00,
envelope_address=0x0E1EED80,
envelope_slot_size=2048,
dfu_partition_output_file="",
dfu_partition_address=0,
dfu_max_caches=4,
Expand Down Expand Up @@ -304,10 +307,11 @@ def test_malformed_envelope(mocker):
with pytest.raises(SUITError):
cmd_image_main(
image="boot",
input_file="some_input",
input_file=["some_input"],
storage_output_file="some_output.hex",
update_candidate_info_address=0x0E1FE000,
envelope_address=0x0E1FF000,
envelope_slot_size=2048,
dfu_partition_output_file="",
dfu_partition_address=0,
dfu_max_caches=0,
Expand Down

0 comments on commit 42489ab

Please sign in to comment.