mirror of
https://github.com/ARM-software/arm-trusted-firmware.git
synced 2025-04-17 18:14:24 +00:00

Transfer List Compiler is a command line tool that enables the static generation of TL's compliant with version 0.9 of the firmware handoff specification. The intent of this tool is to support information passing via the firmware handoff framework to bootloaders that run without preceding images (i.e. `RESET_TO_BL31`). It currently allows for TL's to be statically generated from blobs of data, and modified by removing/adding TE's. Future work will provide support for TL generation from configuration file. Change-Id: Iff670842e34c9ad18eac935248ee2aece43dc533 Signed-off-by: Harrison Mutai <harrison.mutai@arm.com> Co-authored-by: Charlie Bareham <charlie.bareham@arm.com>
205 lines
6.2 KiB
Python
205 lines
6.2 KiB
Python
#!/usr/bin/env python3
|
|
# type: ignore[attr-defined]
|
|
|
|
#
|
|
# Copyright (c) 2024, Arm Limited. All rights reserved.
|
|
#
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
#
|
|
|
|
"""Contains unit tests for the CLI functionality."""
|
|
|
|
from pathlib import Path
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from click.testing import CliRunner
|
|
|
|
from tlc.cli import cli
|
|
from tlc.te import TransferEntry
|
|
from tlc.tl import TransferList
|
|
|
|
|
|
def test_create_empty_tl(tmpdir):
|
|
runner = CliRunner()
|
|
test_file = tmpdir.join("tl.bin")
|
|
|
|
result = runner.invoke(cli, ["create", test_file.strpath])
|
|
assert result.exit_code == 0
|
|
assert TransferList.fromfile(test_file) is not None
|
|
|
|
|
|
def test_create_with_fdt(tmpdir):
|
|
runner = CliRunner()
|
|
fdt = tmpdir.join("fdt.dtb")
|
|
fdt.write_binary(b"\x00" * 100)
|
|
|
|
result = runner.invoke(
|
|
cli,
|
|
[
|
|
"create",
|
|
"--fdt",
|
|
fdt.strpath,
|
|
"--size",
|
|
"1000",
|
|
tmpdir.join("tl.bin").strpath,
|
|
],
|
|
)
|
|
assert result.exit_code == 0
|
|
|
|
|
|
def test_add_single_entry(tlcrunner, tmptlstr):
|
|
tlcrunner.invoke(cli, ["add", "--entry", "0", "/dev/null", tmptlstr])
|
|
|
|
tl = TransferList.fromfile(tmptlstr)
|
|
assert tl is not None
|
|
assert len(tl.entries) == 1
|
|
assert tl.entries[0].id == 0
|
|
|
|
|
|
def test_add_multiple_entries(tlcrunner, tlc_entries, tmptlstr):
|
|
for id, path in tlc_entries:
|
|
tlcrunner.invoke(cli, ["add", "--entry", id, path, tmptlstr])
|
|
|
|
tl = TransferList.fromfile(tmptlstr)
|
|
assert tl is not None
|
|
assert len(tl.entries) == len(tlc_entries)
|
|
|
|
|
|
def test_info(tlcrunner, tmptlstr, tmpfdt):
|
|
tlcrunner.invoke(cli, ["add", "--entry", "0", "/dev/null", tmptlstr])
|
|
tlcrunner.invoke(cli, ["add", "--fdt", tmpfdt.strpath, tmptlstr])
|
|
|
|
result = tlcrunner.invoke(cli, ["info", tmptlstr])
|
|
assert result.exit_code == 0
|
|
assert "signature" in result.stdout
|
|
assert "id" in result.stdout
|
|
|
|
result = tlcrunner.invoke(cli, ["info", "--header", tmptlstr])
|
|
assert result.exit_code == 0
|
|
assert "signature" in result.stdout
|
|
assert "id" not in result.stdout
|
|
|
|
result = tlcrunner.invoke(cli, ["info", "--entries", tmptlstr])
|
|
assert result.exit_code == 0
|
|
assert "signature" not in result.stdout
|
|
assert "id" in result.stdout
|
|
|
|
|
|
def test_raises_max_size_error(tmptlstr, tmpfdt):
|
|
tmpfdt.write_binary(bytes(6000))
|
|
|
|
runner = CliRunner()
|
|
result = runner.invoke(cli, ["create", "--fdt", tmpfdt, tmptlstr])
|
|
|
|
assert result.exception
|
|
assert isinstance(result.exception, MemoryError)
|
|
assert "TL max size exceeded, consider increasing with the option -s" in str(
|
|
result.exception
|
|
)
|
|
assert "TL size has exceeded the maximum allocation" in str(
|
|
result.exception.__cause__
|
|
)
|
|
|
|
|
|
def test_info_get_fdt_offset(tmptlstr, tmpfdt):
|
|
runner = CliRunner()
|
|
with runner.isolated_filesystem():
|
|
runner.invoke(cli, ["create", "--size", "1000", tmptlstr])
|
|
runner.invoke(cli, ["add", "--entry", "1", tmpfdt.strpath, tmptlstr])
|
|
result = runner.invoke(cli, ["info", "--fdt-offset", tmptlstr])
|
|
|
|
assert result.exit_code == 0
|
|
assert result.output.strip("\n").isdigit()
|
|
|
|
|
|
def test_remove_tag(tlcrunner, tmptlstr):
|
|
tlcrunner.invoke(cli, ["add", "--entry", "0", "/dev/null", tmptlstr])
|
|
result = tlcrunner.invoke(cli, ["info", tmptlstr])
|
|
|
|
assert result.exit_code == 0
|
|
assert "signature" in result.stdout
|
|
|
|
tlcrunner.invoke(cli, ["remove", "--tags", "0", tmptlstr])
|
|
tl = TransferList.fromfile(tmptlstr)
|
|
|
|
assert result.exit_code == 0
|
|
assert len(tl.entries) == 0
|
|
|
|
|
|
def test_unpack_tl(tlcrunner, tmptlstr, tmpfdt, tmpdir):
|
|
with tlcrunner.isolated_filesystem(temp_dir=tmpdir):
|
|
tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr])
|
|
tlcrunner.invoke(cli, ["unpack", tmptlstr])
|
|
assert Path("te_0_1.bin").exists()
|
|
|
|
|
|
def test_unpack_multiple_tes(tlcrunner, tlc_entries, tmptlstr, tmpdir):
|
|
with tlcrunner.isolated_filesystem(temp_dir=tmpdir):
|
|
for id, path in tlc_entries:
|
|
tlcrunner.invoke(cli, ["add", "--entry", id, path, tmptlstr])
|
|
|
|
assert all(
|
|
filter(
|
|
lambda te: (Path(tmpdir.strpath) / f"te_{te[0]}.bin").exists(), tlc_entries
|
|
)
|
|
)
|
|
|
|
|
|
def test_unpack_into_dir(tlcrunner, tmpdir, tmptlstr, tmpfdt):
|
|
tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr])
|
|
tlcrunner.invoke(cli, ["unpack", "-C", tmpdir.strpath, tmptlstr])
|
|
|
|
assert (Path(tmpdir.strpath) / "te_0_1.bin").exists()
|
|
|
|
|
|
def test_unpack_into_dir_with_conflicting_tags(tlcrunner, tmpdir, tmptlstr, tmpfdt):
|
|
tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr])
|
|
tlcrunner.invoke(cli, ["add", "--entry", 1, tmpfdt.strpath, tmptlstr])
|
|
tlcrunner.invoke(cli, ["unpack", "-C", tmpdir.strpath, tmptlstr])
|
|
|
|
assert (Path(tmpdir.strpath) / "te_0_1.bin").exists()
|
|
assert (Path(tmpdir.strpath) / "te_1_1.bin").exists()
|
|
|
|
|
|
def test_validate_invalid_signature(tmptlstr, tlcrunner, monkeypatch):
|
|
tl = TransferList()
|
|
tl.signature = 0xDEADBEEF
|
|
|
|
mock_open = lambda tmptlstr, mode: mock.mock_open(read_data=tl.header_to_bytes())()
|
|
monkeypatch.setattr("builtins.open", mock_open)
|
|
|
|
result = tlcrunner.invoke(cli, ["validate", tmptlstr])
|
|
assert result.exit_code != 0
|
|
|
|
|
|
def test_validate_misaligned_entries(tmptlstr, tlcrunner, monkeypatch):
|
|
"""Base address of a TE must be 8-byte aligned."""
|
|
mock_open = lambda tmptlstr, mode: mock.mock_open(
|
|
read_data=TransferList().header_to_bytes()
|
|
+ bytes(5)
|
|
+ TransferEntry(0, 0, bytes(0)).header_to_bytes
|
|
)()
|
|
monkeypatch.setattr("builtins.open", mock_open)
|
|
|
|
result = tlcrunner.invoke(cli, ["validate", tmptlstr])
|
|
|
|
assert result.exit_code == 1
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"version", [0, TransferList.version, TransferList.version + 1, 1 << 8]
|
|
)
|
|
def test_validate_unsupported_version(version, tmptlstr, tlcrunner, monkeypatch):
|
|
tl = TransferList()
|
|
tl.version = version
|
|
|
|
mock_open = lambda tmptlstr, mode: mock.mock_open(read_data=tl.header_to_bytes())()
|
|
monkeypatch.setattr("builtins.open", mock_open)
|
|
|
|
result = tlcrunner.invoke(cli, ["validate", tmptlstr])
|
|
|
|
if version >= TransferList.version and version <= 0xFF:
|
|
assert result.exit_code == 0
|
|
else:
|
|
assert result.exit_code == 1
|