Skip to content

Commit 7babc31

Browse files
committed
Merge patch series "Enable Firmware Handoff CI test on qemu_arm64"
Raymond Mao <raymond.mao@linaro.org> says: This patch series enable Firmware Handoff [1] CI tests on qemu_arm64 by: 1. fetch MbedTLS (v3.6), OP-TEE (v4.7.0) and TF-A (v2.13.0); 2. build bl1 and fip with both Firmware Handoff and Measured Boot enabled; 3. pytest to validate the Firmware Handoff feature via bloblist by checking the existence of expected FDT nodes and TPM events generated and handed over from TF-A/OP-TEE. [1] https://github.com/FirmwareHandoff/firmware_handoff Link: https://lore.kernel.org/r/20251021181703.598342-1-raymond.mao@linaro.org
2 parents 45b7857 + 915f0b2 commit 7babc31

6 files changed

Lines changed: 189 additions & 7 deletions

File tree

.azure-pipelines.yml

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -308,8 +308,18 @@ stages:
308308
/opt/coreboot/cbfstool \${UBOOT_TRAVIS_BUILD_DIR}/coreboot.rom add-flat-binary -f \${UBOOT_TRAVIS_BUILD_DIR}/u-boot.bin -n fallback/payload -c LZMA -l 0x1110000 -e 0x1110000;
309309
fi
310310
# If we have TF-A binaries, we need to use them.
311-
if [[ -d /opt/tf-a/"\${TEST_PY_BD}" ]]; then
312-
cp /opt/tf-a/"\${TEST_PY_BD}"/fip.bin /opt/tf-a/"\${TEST_PY_BD}"/bl1.bin /tmp;
311+
tfa_dir=""
312+
rm -f /tmp/fip.bin
313+
rm -f /tmp/bl1.bin
314+
if [[ -d /opt/tf-a/"\${TEST_PY_BD}\${TEST_PY_ID//--id /_}" ]]; then
315+
tfa_dir="/opt/tf-a/\${TEST_PY_BD}\${TEST_PY_ID//--id /_}";
316+
elif [[ -d /opt/tf-a/"\${TEST_PY_BD}" ]]; then
317+
tfa_dir="/opt/tf-a/\${TEST_PY_BD}";
318+
fi
319+
if [[ -n "\$tfa_dir" ]]; then
320+
cp "\$tfa_dir"/fip.bin "\$tfa_dir"/bl1.bin /tmp/;
321+
fi
322+
if [ -f /tmp/fip.bin ] && [ -f /tmp/bl1.bin ]; then
313323
export fip=/tmp/fip.bin;
314324
export bl1=/tmp/bl1.bin;
315325
export PATH=/opt/Base_RevC_AEMvA_pkg/models/Linux64_GCC-9.3:\${PATH};
@@ -467,6 +477,10 @@ stages:
467477
qemu_arm64_lwip:
468478
TEST_PY_BD: "qemu_arm64_lwip"
469479
TEST_PY_TEST_SPEC: "test_net_dhcp or test_net_ping or test_net_tftpboot"
480+
qemu_arm64_tfa_fw_handoff:
481+
TEST_PY_BD: "qemu_arm64"
482+
TEST_PY_ID: "--id fw_handoff_tfa_optee"
483+
TEST_PY_TEST_SPEC: "test_fw_handoff"
470484
qemu_arm_sbsa_ref:
471485
TEST_PY_BD: "qemu-arm-sbsa"
472486
TEST_PY_TEST_SPEC: "not sleep"

.gitlab-ci.yml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,18 @@ stages:
9797
/opt/coreboot/cbfstool ${UBOOT_TRAVIS_BUILD_DIR}/coreboot.rom add-flat-binary -f ${UBOOT_TRAVIS_BUILD_DIR}/u-boot.bin -n fallback/payload -c LZMA -l 0x1110000 -e 0x1110000;
9898
fi
9999
# If we have TF-A binaries, we need to use them.
100-
- if [[ -d /opt/tf-a/"${TEST_PY_BD}" ]]; then
101-
cp /opt/tf-a/"${TEST_PY_BD}"/fip.bin /opt/tf-a/"${TEST_PY_BD}"/bl1.bin /tmp/;
100+
- tfa_dir=""
101+
- rm -f /tmp/fip.bin
102+
- rm -f /tmp/bl1.bin
103+
- if [[ -d /opt/tf-a/"${TEST_PY_BD}${TEST_PY_ID//--id /_}" ]]; then
104+
tfa_dir="/opt/tf-a/${TEST_PY_BD}${TEST_PY_ID//--id /_}";
105+
elif [[ -d /opt/tf-a/"${TEST_PY_BD}" ]]; then
106+
tfa_dir="/opt/tf-a/${TEST_PY_BD}";
107+
fi
108+
- if [[ -n "$tfa_dir" ]]; then
109+
cp "$tfa_dir"/fip.bin "$tfa_dir"/bl1.bin /tmp/;
110+
fi
111+
- if [ -f /tmp/fip.bin ] && [ -f /tmp/bl1.bin ]; then
102112
export fip=/tmp/fip.bin;
103113
export bl1=/tmp/bl1.bin;
104114
export PATH=/opt/Base_RevC_AEMvA_pkg/models/Linux64_GCC-9.3:${PATH};
@@ -399,6 +409,13 @@ qemu_arm64_lwip test.py:
399409
TEST_PY_TEST_SPEC: "test_net_dhcp or test_net_ping or test_net_tftpboot"
400410
<<: *buildman_and_testpy_dfn
401411

412+
qemu_arm64_tfa_fw_handoff test.py:
413+
variables:
414+
TEST_PY_BD: "qemu_arm64"
415+
TEST_PY_ID: "--id fw_handoff_tfa_optee"
416+
TEST_PY_TEST_SPEC: "test_fw_handoff"
417+
<<: *buildman_and_testpy_dfn
418+
402419
qemu_arm_sbsa test.py:
403420
variables:
404421
TEST_PY_BD: "qemu-arm-sbsa"

configs/qemu_arm64_defconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ CONFIG_CMD_SMBIOS=y
3131
CONFIG_CMD_BOOTZ=y
3232
CONFIG_CMD_BOOTEFI_SELFTEST=y
3333
CONFIG_CMD_NVEDIT_EFI=y
34+
CONFIG_CMD_BLOBLIST=y
3435
CONFIG_CMD_DFU=y
3536
CONFIG_CMD_MTD=y
3637
CONFIG_CMD_PCI=y
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
test_fw_handoff
2+
===============
3+
4+
.. automodule:: test_fw_handoff
5+
:synopsis:
6+
:member-order: bysource
7+
:members:
8+
:undoc-members:

test/py/tests/test_fw_handoff.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
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}"

tools/docker/Dockerfile

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,8 +122,10 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
122122
python-is-python3 \
123123
python2.7 \
124124
python3 \
125+
python3-cryptography \
125126
python3-dev \
126127
python3-pip \
128+
python3-pyelftools \
127129
python3-sphinx \
128130
python3-tomli \
129131
python3-venv \
@@ -227,10 +229,24 @@ RUN git clone https://gitlab.com/qemu-project/qemu.git /tmp/qemu && \
227229
make -j$(nproc) all install && \
228230
rm -rf /tmp/qemu
229231

230-
# Build fiptool
231-
RUN git clone https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git /tmp/tf-a && \
232+
# Build OP-TEE for qemu_arm64
233+
RUN git clone --depth=1 https://github.com/OP-TEE/optee_os.git /tmp/optee_os \
234+
-b 4.7.0 && \
235+
cd /tmp/optee_os/ && \
236+
make CROSS_COMPILE32=/opt/gcc-${TCVER}-nolibc/arm-linux-gnueabi/bin/arm-linux-gnueabi- \
237+
CROSS_COMPILE64=/opt/gcc-${TCVER}-nolibc/aarch64-linux/bin/aarch64-linux- \
238+
CFG_TRANSFER_LIST=y CFG_MAP_EXT_DT_SECURE=y \
239+
PLATFORM=vexpress-qemu_armv8a CFG_RPMB_FS=y \
240+
CFG_RPMB_WRITE_KEY=y CFG_RPMB_TESTKEY=y \
241+
CFG_CORE_HEAP_SIZE=524288 \
242+
CFG_REE_FS=n CFG_CORE_ARM64_PA_BITS=48 \
243+
CFG_TEE_CORE_LOG_LEVEL=2
244+
245+
# Build fiptool, bl1 and fip for fvp and qemu_arm64
246+
RUN git clone --depth=1 -b mbedtls-3.6 https://github.com/ARMmbed/mbedtls.git /tmp/mbedtls
247+
RUN git clone --depth=1 https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git /tmp/tf-a \
248+
-b v2.13.0 && \
232249
cd /tmp/tf-a/ && \
233-
git checkout v2.12.0 && \
234250
make CROSS_COMPILE=/opt/gcc-${TCVER}-nolibc/aarch64-linux/bin/aarch64-linux- \
235251
PLAT=fvp BL33=/dev/null -j$(nproc) all fip && \
236252
mkdir -p /usr/local/bin /opt/tf-a/vexpress_fvp && \
@@ -243,6 +259,24 @@ RUN git clone https://git.trustedfirmware.org/TF-A/trusted-firmware-a.git /tmp/t
243259
mkdir -p /opt/tf-a/vexpress_fvp_bloblist && \
244260
cp build/fvp/release/fip.bin build/fvp/release/bl1.bin \
245261
/opt/tf-a/vexpress_fvp_bloblist/ && \
262+
make CROSS_COMPILE=/opt/gcc-${TCVER}-nolibc/aarch64-linux/bin/aarch64-linux- \
263+
PLAT=qemu \
264+
BL33=/dev/null \
265+
BL32=/tmp/optee_os/out/arm-plat-vexpress/core/tee-header_v2.bin \
266+
BL32_EXTRA1=/tmp/optee_os/out/arm-plat-vexpress/core/tee-pager_v2.bin \
267+
BL32_EXTRA2=/tmp/optee_os/out/arm-plat-vexpress/core/tee-pageable_v2.bin \
268+
BL32_RAM_LOCATION=tdram SPD=opteed \
269+
TRANSFER_LIST=1 E=0 \
270+
MEASURED_BOOT=1 \
271+
EVENT_LOG_LEVEL=10 \
272+
MBOOT_EL_HASH_ALG=sha256 \
273+
MBEDTLS_DIR=/tmp/mbedtls \
274+
-j$(nproc) all fip && \
275+
mkdir -p /opt/tf-a/qemu_arm64_fw_handoff_tfa_optee && \
276+
cp build/qemu/release/fip.bin build/qemu/release/bl1.bin \
277+
/opt/tf-a/qemu_arm64_fw_handoff_tfa_optee/ && \
278+
rm -rf /tmp/optee_os && \
279+
rm -rf /tmp/mbedtls && \
246280
rm -rf /tmp/tf-a
247281

248282
# Download the Arm Architecture FVP platform. This file is double compressed.

0 commit comments

Comments
 (0)