arm-trusted-firmware/tools/tlc/tests/test_transfer_list.py
J-Alves c4c8e26a69 feat(tlc): add --align argument
Extended the command line interface to receive an alignment
argument.

TLC tool will align the data of the TEs accordingly.

Signed-off-by: J-Alves <joao.alves@arm.com>
Change-Id: I281b0b4c1851d58377bf6b31fcee03ee2f53367b
2024-12-09 18:21:11 +00:00

280 lines
8.3 KiB
Python

#!/usr/bin/env python3
#
# Copyright (c) 2024, Arm Limited. All rights reserved.
#
# SPDX-License-Identifier: BSD-3-Clause
#
"""Contains unit tests for the types TransferEntry and TransferList."""
import math
from random import randint
import pytest
from tlc.te import TransferEntry
from tlc.tl import TransferList
large_data = 0xDEADBEEF.to_bytes(4, "big")
small_data = 0x1234.to_bytes(3, "big")
test_entries = [
(0, b""),
(1, small_data),
(1, large_data),
(0xFFFFFF, small_data),
(0xFFFFFF, large_data),
]
@pytest.mark.parametrize(
"size,csum",
[
(-1, None),
(0x18, 0x9E),
(0x1000, 0xA6),
(0x2000, 0x96),
(0x4000, 0x76),
],
)
def test_make_transfer_list(size, csum):
if size < 8:
with pytest.raises(AssertionError):
tl = TransferList(size)
else:
tl = TransferList(size)
assert tl.signature == 0x4A0FB10B
assert not tl.entries
assert tl.sum_of_bytes() == 0
assert tl.checksum == csum
def test_add_transfer_entry(random_entries):
tl = TransferList(0x1000)
# Add a single entry and check it's in the list of entries
te = tl.add_transfer_entry(1, bytes(100))
assert te in tl.entries
assert tl.size % 8 == 0
# Add a range of tag id's
for id, data in random_entries(50, 1):
te = tl.add_transfer_entry(id, data)
assert te in tl.entries
assert tl.size % 8 == 0
@pytest.mark.parametrize("align", [4, 6, 12, 13])
def test_add_transfer_entry_with_align(align, random_entries, random_entry):
tl = TransferList(0xF00000)
id, data = random_entry(4)
tl.add_transfer_entry(id, data)
# Add an entry with a larger alignment requirement
_, data = random_entry(4)
te = tl.add_transfer_entry(1, data, data_align=align)
assert (te.offset + te.hdr_size) % (1 << align) == 0
assert tl.alignment == align
# Add some more entries and ensure the alignment is preserved
for id, data in random_entries(5, 0x200):
te = tl.add_transfer_entry(id, data, data_align=align)
assert (te.offset + te.hdr_size) % (1 << align) == 0
assert tl.alignment == align
@pytest.mark.parametrize(
("tag_id", "data"),
[
(-1, None), # tag out of range
(1, None), # no data provided
(1, bytes(8000)), # very large data > total size
(0x100000, b"0dd0edfe"), # tag out of range
],
)
def test_add_out_of_range_transfer_entry(tag_id, data):
tl = TransferList()
with pytest.raises(Exception):
tl.add_transfer_entry(tag_id, data)
@pytest.mark.parametrize(("tag_id", "data"), test_entries)
def test_calculate_te_sum_of_bytes(tag_id, data):
te = TransferEntry(tag_id, len(data), data)
csum = (
sum(data)
+ sum(len(data).to_bytes(4, "big"))
+ te.hdr_size
+ sum(tag_id.to_bytes(4, "big"))
) % 256
assert te.sum_of_bytes == csum
def test_calc_tl_checksum(tmpdir, random_entries):
tl_file = tmpdir.join("tl.bin")
tl = TransferList(0x1000)
for id, data in random_entries(10):
tl.add_transfer_entry(id, data)
assert sum(tl.to_bytes()) % 256 == 0
# Write the transfer list to a file and check that the sum of bytes is 0
tl.write_to_file(tl_file)
assert sum(tl_file.read_binary()) % 256 == 0
def test_empty_transfer_list_blob(tmpdir):
"""Check that we can correctly create a transfer list header."""
test_file = tmpdir.join("test_tl_blob.bin")
tl = TransferList()
tl.write_to_file(test_file)
with open(test_file, "rb") as f:
assert f.read(tl.hdr_size) == tl.header_to_bytes()
@pytest.mark.parametrize(("tag_id", "data"), test_entries)
def test_single_te_transfer_list(tag_id, data, tmpdir):
"""Check that we can create a complete TL with a single TE."""
test_file = tmpdir.join("test_tl_blob.bin")
tl = TransferList(0x1000)
tl.add_transfer_entry(tag_id, data)
tl.write_to_file(test_file)
te = tl.entries[0]
with open(test_file, "rb") as f:
assert f.read(tl.hdr_size) == tl.header_to_bytes()
assert int.from_bytes(f.read(3), "little") == te.id
assert int.from_bytes(f.read(1), "little") == te.hdr_size
assert int.from_bytes(f.read(4), "little") == te.data_size
assert f.read(te.data_size) == te.data
def test_write_multiple_tes_to_file(tmpdir, random_entries, random_entry):
"""Check that we can create a TL with multiple TE's."""
test_file = tmpdir.join("test_tl_blob.bin")
tl = TransferList(0x4000)
_test_entries = list(random_entries())
for tag_id, data in _test_entries:
tl.add_transfer_entry(tag_id, data)
# Add a few entries with special alignment requirements
blob_id, blob = random_entry(0x200)
tl.add_transfer_entry(blob_id, blob, data_align=12)
tl.write_to_file(test_file)
with open(test_file, "rb") as f:
assert f.read(tl.hdr_size) == tl.header_to_bytes()
# Ensure that TE's have the correct alignment
for tag_id, data in _test_entries:
f.seek(int(math.ceil(f.tell() / 8) * 8))
assert int.from_bytes(f.read(3), "little") == tag_id
assert int.from_bytes(f.read(1), "little") == TransferEntry.hdr_size
# Make sure the data in the TE matches the data in the original case
data_size = int.from_bytes(f.read(4), "little")
assert f.read(data_size) == data
f.seek(int(math.ceil(f.tell() / (1 << 12)) * (1 << 12)) - 8)
assert int.from_bytes(f.read(3), "little") == blob_id
assert int.from_bytes(f.read(1), "little") == TransferEntry.hdr_size
# Make sure the data in the TE matches the data in the original case
data_size = int.from_bytes(f.read(4), "little")
assert f.read(data_size) == blob
# padding is added to align TE's, make sure padding is added to the size of
# the TL by checking we don't overflow.
assert f.tell() <= tl.size
def test_read_empty_transfer_list_from_file(tmpdir):
test_file = tmpdir.join("test_tl_blob.bin")
original_tl = TransferList(0x1000)
original_tl.write_to_file(test_file)
# Read the contents of the file we just wrote
tl = TransferList.fromfile(test_file)
assert tl.header_to_bytes() == original_tl.header_to_bytes()
assert tl.sum_of_bytes() == 0
def test_read_single_transfer_list_from_file(tmpdir):
test_file = tmpdir.join("test_tl_blob.bin")
original_tl = TransferList(0x1000)
original_tl.add_transfer_entry(test_entries[0][0], test_entries[0][1])
original_tl.write_to_file(test_file)
# Read the contents of the file we just wrote
tl = TransferList.fromfile(test_file)
assert tl.entries
te = tl.entries[0]
assert te.id == test_entries[0][0]
assert te.data == test_entries[0][1]
assert tl.sum_of_bytes() == 0
def test_read_multiple_transfer_list_from_file(tmpdir):
test_file = tmpdir.join("test_tl_blob.bin")
original_tl = TransferList(0x1000)
for tag_id, data in test_entries:
original_tl.add_transfer_entry(tag_id, data)
original_tl.write_to_file(test_file)
# Read the contents of the file we just wrote
tl = TransferList.fromfile(test_file)
# The TE we derive from the file might have a an associated offset, compare
# the TE's based on the header in bytes, which doesn't account for this.
for te0, te1 in zip(tl.entries, original_tl.entries):
assert te0.header_to_bytes() == te1.header_to_bytes()
assert tl.sum_of_bytes() == 0
def test_remove_tag(random_entry):
"""Adds a transfer entry and remove it, size == transfer list header."""
tl = TransferList(0x100)
id, data = random_entry(tl.total_size // 2)
te = tl.add_transfer_entry(id, data)
assert te in tl.entries
tl.remove_tag(id)
assert not tl.get_entry(id) and te not in tl.entries
assert tl.size == tl.hdr_size
def test_get_fdt_offset(tmpdir):
tl = TransferList(0x1000)
tl.add_transfer_entry(1, 0xEDFE0DD0.to_bytes(4, "big"))
f = tmpdir.join("blob.bin")
tl.write_to_file(f)
blob_tl = TransferList.fromfile(f)
assert blob_tl.hdr_size + TransferEntry.hdr_size == blob_tl.get_entry_data_offset(1)
def test_get_missing_fdt_offset(tmpdir):
tl = TransferList(0x1000)
f = tmpdir.join("blob.bin")
tl.write_to_file(f)
blob_tl = TransferList.fromfile(f)
with pytest.raises(ValueError):
blob_tl.get_entry_data_offset(1)