|
| 1 | +# SPDX-License-Identifier: GPL-2.0+ |
| 2 | +# |
| 3 | +# Copyright (c) 2025 Linaro Limited |
| 4 | +# Author: Raymond Mao <raymond.mao@linaro.org> |
| 5 | +# |
| 6 | +# Validate Firmware Handoff from TF-A and OP-TEE |
| 7 | + |
| 8 | +""" |
| 9 | +Note: This test relies on boardenv_* containing configuration values to define |
| 10 | +whether Firmware Handoff is enabled for testing. Without this, this test |
| 11 | +will be automatically skipped. |
| 12 | +
|
| 13 | +For example: |
| 14 | +
|
| 15 | +.. code-block:: python |
| 16 | +
|
| 17 | + # Boolean indicating whether Firmware Handoff is enabled on this board. |
| 18 | + # This variable may be omitted if its value is False. |
| 19 | + env__firmware_handoff_enabled = True |
| 20 | +""" |
| 21 | + |
| 22 | +import pytest |
| 23 | +import re |
| 24 | + |
| 25 | +def _norm_ws(s: str) -> str: |
| 26 | + """Normalize whitespace for robust comparisons.""" |
| 27 | + return re.sub(r"\s+", " ", s).strip() |
| 28 | + |
| 29 | +@pytest.mark.buildconfigspec("bloblist") |
| 30 | +@pytest.mark.buildconfigspec("cmd_bloblist") |
| 31 | +@pytest.mark.buildconfigspec("of_control") |
| 32 | +@pytest.mark.buildconfigspec("cmd_fdt") |
| 33 | +def test_fw_handoff_dt(ubman): |
| 34 | + """Validate FDT handoff via bloblist.""" |
| 35 | + |
| 36 | + fh_en = ubman.config.env.get('env__firmware_handoff_enabled', False) |
| 37 | + if not fh_en: |
| 38 | + pytest.skip('Firmware Handoff is disabled') |
| 39 | + |
| 40 | + bloblist = ubman.run_command("bloblist list") |
| 41 | + blob_fdt = re.search(r"^([0-9a-fA-F]+)\s+[0-9a-fA-F]+\s+1\s+Control FDT\s*$", |
| 42 | + bloblist, re.MULTILINE) |
| 43 | + assert blob_fdt, "Control FDT entry not found in bloblist" |
| 44 | + |
| 45 | + blob_fdt_addr = int(blob_fdt.group(1), 16) |
| 46 | + ubman.run_command(f"fdt addr {blob_fdt_addr:x}") |
| 47 | + |
| 48 | + reserved_a = ubman.run_command("fdt print /reserved-memory") |
| 49 | + firmware_a = ubman.run_command("fdt print /firmware") |
| 50 | + |
| 51 | + fdt_addr_out = ubman.run_command("echo $fdt_addr") |
| 52 | + fdt_addr_match = re.search(r"(?:0x)?([0-9a-fA-F]+)", fdt_addr_out) |
| 53 | + assert fdt_addr_match, "Could not parse $fdt_addr" |
| 54 | + |
| 55 | + fdt_addr = int(fdt_addr_match.group(1), 16) |
| 56 | + ubman.run_command(f"fdt addr {fdt_addr:x}") |
| 57 | + |
| 58 | + reserved_b = ubman.run_command("fdt print /reserved-memory") |
| 59 | + firmware_b = ubman.run_command("fdt print /firmware") |
| 60 | + |
| 61 | + # Normalize whitespace & compare |
| 62 | + assert _norm_ws(reserved_a) == _norm_ws(reserved_b), \ |
| 63 | + "reserved-memory blocks differ between Control FDT and $fdt_addr FDT" |
| 64 | + assert _norm_ws(firmware_a) == _norm_ws(firmware_b), \ |
| 65 | + "firmware blocks differ between Control FDT and $fdt_addr FDT" |
| 66 | + |
| 67 | +@pytest.mark.buildconfigspec("bloblist") |
| 68 | +@pytest.mark.buildconfigspec("cmd_bloblist") |
| 69 | +@pytest.mark.buildconfigspec("cmd_memory") |
| 70 | +def test_fw_handoff_eventlog(ubman): |
| 71 | + """Validate TPM event log handoff via bloblist.""" |
| 72 | + |
| 73 | + fh_en = ubman.config.env.get('env__firmware_handoff_enabled', False) |
| 74 | + if not fh_en: |
| 75 | + pytest.skip('Firmware Handoff is disabled') |
| 76 | + |
| 77 | + # Get the address and size of eventlog from the bloblist |
| 78 | + bloblist_output = ubman.run_command("bloblist list") |
| 79 | + evt_addr = None |
| 80 | + evt_size = None |
| 81 | + for line in bloblist_output.splitlines(): |
| 82 | + if "TPM event log" in line: |
| 83 | + parts = line.strip().split() |
| 84 | + evt_addr = int(parts[0], 16) |
| 85 | + evt_size = int(parts[1], 16) |
| 86 | + break |
| 87 | + |
| 88 | + assert evt_addr is not None and evt_size is not None, \ |
| 89 | + "TPM event log not found in bloblist" |
| 90 | + |
| 91 | + # Read byte from memory and extract printable ASCII from each line |
| 92 | + md_output = ubman.run_command(f"md.b {evt_addr:x} {evt_size}") |
| 93 | + ascii_log = "" |
| 94 | + for line in md_output.splitlines(): |
| 95 | + match = re.search(r'([0-9a-f]+:.*?)((?:\s[0-9a-f]{2}){1,16})\s+(.*)', line) |
| 96 | + if match: |
| 97 | + ascii_part = match.group(3).strip() |
| 98 | + ascii_log += ascii_part |
| 99 | + |
| 100 | + # The events created by TF-A are expected |
| 101 | + expected_keywords = [ |
| 102 | + "SECURE_RT_EL3", |
| 103 | + "SECURE_RT_EL1_OPTEE", |
| 104 | + "SECURE_RT_EL1_OPTEE_EXTRA1" |
| 105 | + ] |
| 106 | + |
| 107 | + for keyword in expected_keywords: |
| 108 | + assert keyword in ascii_log, f"Missing expected event: {keyword}" |
0 commit comments