From f974e9b626323a940f308ba7174dff0607827aed Mon Sep 17 00:00:00 2001 From: Raymond Mao Date: Tue, 21 Oct 2025 11:16:55 -0700 Subject: [PATCH] pytest: add test script to validate Firmware Handoff Add test cases to validate FDT and TPM eventlog handoff from TF-A and OP-TEE via bloblist. For FDT, the nodes 'reserved-memory' and 'firmware' appended by OP-TEE indicates a successful handoff. For TPM eventlog, the events 'SECURE_RT_EL3', 'SECURE_RT_EL1_OPTEE' and 'SECURE_RT_EL1_OPTEE_EXTRA1' created by TF-A indicates a successful handoff. Signed-off-by: Raymond Mao Reviewed-by: Tom Rini --- doc/develop/pytest/test_fw_handoff.rst | 8 ++ test/py/tests/test_fw_handoff.py | 108 +++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 doc/develop/pytest/test_fw_handoff.rst create mode 100644 test/py/tests/test_fw_handoff.py diff --git a/doc/develop/pytest/test_fw_handoff.rst b/doc/develop/pytest/test_fw_handoff.rst new file mode 100644 index 00000000000..af926f684c6 --- /dev/null +++ b/doc/develop/pytest/test_fw_handoff.rst @@ -0,0 +1,8 @@ +test_fw_handoff +=============== + +.. automodule:: test_fw_handoff + :synopsis: + :member-order: bysource + :members: + :undoc-members: diff --git a/test/py/tests/test_fw_handoff.py b/test/py/tests/test_fw_handoff.py new file mode 100644 index 00000000000..45f154665ef --- /dev/null +++ b/test/py/tests/test_fw_handoff.py @@ -0,0 +1,108 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# Copyright (c) 2025 Linaro Limited +# Author: Raymond Mao +# +# Validate Firmware Handoff from TF-A and OP-TEE + +""" +Note: This test relies on boardenv_* containing configuration values to define +whether Firmware Handoff is enabled for testing. Without this, this test +will be automatically skipped. + +For example: + +.. code-block:: python + + # Boolean indicating whether Firmware Handoff is enabled on this board. + # This variable may be omitted if its value is False. + env__firmware_handoff_enabled = True +""" + +import pytest +import re + +def _norm_ws(s: str) -> str: + """Normalize whitespace for robust comparisons.""" + return re.sub(r"\s+", " ", s).strip() + +@pytest.mark.buildconfigspec("bloblist") +@pytest.mark.buildconfigspec("cmd_bloblist") +@pytest.mark.buildconfigspec("of_control") +@pytest.mark.buildconfigspec("cmd_fdt") +def test_fw_handoff_dt(ubman): + """Validate FDT handoff via bloblist.""" + + fh_en = ubman.config.env.get('env__firmware_handoff_enabled', False) + if not fh_en: + pytest.skip('Firmware Handoff is disabled') + + bloblist = ubman.run_command("bloblist list") + blob_fdt = re.search(r"^([0-9a-fA-F]+)\s+[0-9a-fA-F]+\s+1\s+Control FDT\s*$", + bloblist, re.MULTILINE) + assert blob_fdt, "Control FDT entry not found in bloblist" + + blob_fdt_addr = int(blob_fdt.group(1), 16) + ubman.run_command(f"fdt addr {blob_fdt_addr:x}") + + reserved_a = ubman.run_command("fdt print /reserved-memory") + firmware_a = ubman.run_command("fdt print /firmware") + + fdt_addr_out = ubman.run_command("echo $fdt_addr") + fdt_addr_match = re.search(r"(?:0x)?([0-9a-fA-F]+)", fdt_addr_out) + assert fdt_addr_match, "Could not parse $fdt_addr" + + fdt_addr = int(fdt_addr_match.group(1), 16) + ubman.run_command(f"fdt addr {fdt_addr:x}") + + reserved_b = ubman.run_command("fdt print /reserved-memory") + firmware_b = ubman.run_command("fdt print /firmware") + + # Normalize whitespace & compare + assert _norm_ws(reserved_a) == _norm_ws(reserved_b), \ + "reserved-memory blocks differ between Control FDT and $fdt_addr FDT" + assert _norm_ws(firmware_a) == _norm_ws(firmware_b), \ + "firmware blocks differ between Control FDT and $fdt_addr FDT" + +@pytest.mark.buildconfigspec("bloblist") +@pytest.mark.buildconfigspec("cmd_bloblist") +@pytest.mark.buildconfigspec("cmd_memory") +def test_fw_handoff_eventlog(ubman): + """Validate TPM event log handoff via bloblist.""" + + fh_en = ubman.config.env.get('env__firmware_handoff_enabled', False) + if not fh_en: + pytest.skip('Firmware Handoff is disabled') + + # Get the address and size of eventlog from the bloblist + bloblist_output = ubman.run_command("bloblist list") + evt_addr = None + evt_size = None + for line in bloblist_output.splitlines(): + if "TPM event log" in line: + parts = line.strip().split() + evt_addr = int(parts[0], 16) + evt_size = int(parts[1], 16) + break + + assert evt_addr is not None and evt_size is not None, \ + "TPM event log not found in bloblist" + + # Read byte from memory and extract printable ASCII from each line + md_output = ubman.run_command(f"md.b {evt_addr:x} {evt_size}") + ascii_log = "" + for line in md_output.splitlines(): + match = re.search(r'([0-9a-f]+:.*?)((?:\s[0-9a-f]{2}){1,16})\s+(.*)', line) + if match: + ascii_part = match.group(3).strip() + ascii_log += ascii_part + + # The events created by TF-A are expected + expected_keywords = [ + "SECURE_RT_EL3", + "SECURE_RT_EL1_OPTEE", + "SECURE_RT_EL1_OPTEE_EXTRA1" + ] + + for keyword in expected_keywords: + assert keyword in ascii_log, f"Missing expected event: {keyword}" -- 2.47.3