Compare commits

..

5 Commits
efis ... tpm3

Author SHA1 Message Date
Simon Glass
a0dc7c9058 test: tpm: Skip failing tests on coral
These tests have been failing for some months. Disable them so that a CI
run can pass on coral. Further work will be needed to see how to make
them pass.

Series-changes: 3
- Add new patch to skip failing tests on coral

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-05-24 12:59:04 +01:00
Simon Glass
c73260430b tpm: Update cr50 to permit repeated init
The cr50 does not handle being inited multiple times. Add a check for
this and skip the failing command.

Series-changes: 3
- Add new patch to update cr50 for repeated init

Signed-off-by: Simon Glass <sjg@chromium.org>
2025-05-24 12:59:04 +01:00
Simon Glass
e6456b3ba2 tpm: Drop unwanted special cases for sandbox
These don't seem to be needed.

Add a few notes about what to do next. Also mention parallel tests in
at the top of thefile.

Series-to: u-boot
Series-cc: ilias, trini
Series-links: 2:379801
Series-version: 3
Cover-letter:
tpm: Start to tidy up TPM tests
This series is a starting point only. It tries to provide some direction
for how the TPM tests should be run on real hardware and on sandbox.

For sandbox, things are relatively easy since the TPM is reset before
each test. Tests should start up the TPM before doing anything. Tests
can be run in parallel, which is fine because tests are independent.

For real hardware, tests cannot be made independent, other than by
resetting the board, which if the hardware is correct, resets the TPM.
So there may be more work to do to figure that out. The approach taken
in this series for real hardware is to have a few tests which do init,
then have the rest of the tests assume that the init is done. Tests
that depend on the TPM already being inited can use 'tpm autostart'
which works OK on sandbox and real hardware.
END
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>

Change-Id: I3c84965a4b5e3019562b86a47b34ae4b12469070
2025-05-24 12:59:04 +01:00
Simon Glass
5cb7072b08 tpm: Convert sandbox-focussed tests to C
Some of the Python tests are a pain because they don't reset the TPM
state before each test. Driver model tests do this, so convert the
tests to C.

This means that these tests won't run on real hardware, but we have
tests which do TPM init, so there is still enough coverage.

Rename and update the Python tpm_init test to use 'tpm autostart',
since this deals with starting up ready for the tests below.

Series-changes: 3
- Use 'check' instead of 'test' when naming test helpers
- Add missing tpm_self_test_full() call

Series-changes: 2
- Keep test_tpm2_continue_self_test()

Change-Id: I8ff77e0c1288f6a4da564e9454eb223f72809839
Signed-off-by: Simon Glass <sjg@chromium.org>
2025-05-24 12:59:03 +01:00
Simon Glass
0f7489a847 tpm: sandbox: Support self-test continue in emulator
Add support for the self-test continue command in the TPM v1.2 emulator,
to match the functionality in the TPM v2 emulator.

Change-Id: I2fad865da519b36eb7d5879bb9ab552db8055e1f
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-by: Ilias Apalodimas <ilias.apalodimas@linaro.org>
2025-05-24 10:25:09 +01:00
11 changed files with 140 additions and 142 deletions

View File

@@ -2237,7 +2237,7 @@ CLEAN_FILES += include/autoconf.mk* include/bmp_logo.h include/bmp_logo_data.h \
itb.fit.fit itb.fit.itb itb.map spl.map mkimage-out.rom.mkimage \
mkimage.rom.mkimage mkimage-in-simple-bin* rom.map simple-bin* \
idbloader-spi.img lib/efi_loader/helloworld_efi.S *.itb \
Test* capsule*.*.efi-capsule capsule*.map
Test* capsule*.*.efi-capsule capsule*.map capsule_esl_file
# Directories & files removed with 'make mrproper'
MRPROPER_DIRS += include/config include/generated spl tpl vpl \

View File

@@ -737,9 +737,13 @@ static int cr50_i2c_report_state(struct udevice *dev, char *str, int str_max)
static int cr50_i2c_open(struct udevice *dev)
{
struct cr50_priv *priv = dev_get_priv(dev);
char buf[80];
int ret;
if (priv->locality != -1)
return -EBUSY;
ret = process_reset(dev);
if (ret)
return log_msg_ret("reset", ret);

View File

@@ -221,6 +221,7 @@ static int sandbox_tpm_xfer(struct udevice *dev, const uint8_t *sendbuf,
case 0x72: /* physical set deactivated */
case 0x99: /* startup */
case 0x50: /* self test full */
case 0x53: /* self test continue */
case 0x4000000a: /* assert physical presence */
*recv_len = 12;
memset(recvbuf, '\0', *recv_len);

View File

@@ -27,6 +27,8 @@ extern char __efi_helloworld_begin[];
extern char __efi_helloworld_end[];
extern char __efi_var_file_begin[];
extern char __efi_var_file_end[];
extern char __efi_capsule_sig_begin[];
extern char __efi_capsule_sig_end[];
/* Private data used by of-platdata devices/uclasses */
extern char __priv_data_start[], __priv_data_end[];

View File

@@ -29,6 +29,7 @@ obj-y += efi_boottime.o
obj-y += efi_helper.o
obj-$(CONFIG_EFI_HAVE_CAPSULE_SUPPORT) += efi_capsule.o
obj-$(CONFIG_EFI_CAPSULE_FIRMWARE) += efi_firmware.o
obj-$(CONFIG_EFI_CAPSULE_AUTHENTICATE) += efi_capsule_key.o
obj-y += efi_console.o
obj-y += efi_device_path.o
obj-$(CONFIG_EFI_DEVICE_PATH_TO_TEXT) += efi_device_path_to_text.o
@@ -74,6 +75,23 @@ obj-$(CONFIG_EFI_ECPT) += efi_conformance.o
EFI_VAR_SEED_FILE := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE))
$(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
ifeq ($(CONFIG_EFI_CAPSULE_AUTHENTICATE),y)
capsule_crt_path=($(subst $(quote),,$(CONFIG_EFI_CAPSULE_CRT_FILE)))
capsule_crt_full=$(srctree)/$(subst $(quote),,$(CONFIG_EFI_CAPSULE_CRT_FILE))
quiet_cmd_capsule_esl_gen = CAPSULE_ESL_GEN $@
cmd_capsule_esl_gen = cert-to-efi-sig-list $(capsule_crt_full) $@
$(srctree)/capsule_esl_file: FORCE
@if [ ! -e "$(capsule_crt_full)" ]; then \
echo "ERROR: path $(capsule_crt_full) is invalid." >&2; \
echo "EFI CONFIG_EFI_CAPSULE_CRT_FILE must be specified when CONFIG_EFI_CAPSULE_AUTHENTICATE is enabled." >&2; \
exit 1; \
fi
$(call cmd,capsule_esl_gen)
$(obj)/efi_capsule.o: $(srctree)/capsule_esl_file FORCE
asflags-y += -DCAPSULE_ESL_PATH=\"$(srctree)/capsule_esl_file\"
endif
# Set the C flags to add and remove for each app
$(foreach f,$(apps-y),\
$(eval CFLAGS_$(f).o := $(CFLAGS_EFI) -Os -ffreestanding)\

View File

@@ -1,11 +0,0 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* Devicetree file with the public key EFI Signature List(ESL)
* node. This file is used to generate the dtsi file to be
* included into the DTB.
*/
/ {
signature {
capsule-key = /incbin/("ESL_BIN_FILE");
};
};

View File

@@ -22,6 +22,7 @@
#include <asm/global_data.h>
#include <u-boot/uuid.h>
#include <asm/sections.h>
#include <crypto/pkcs7.h>
#include <crypto/pkcs7_parser.h>
#include <linux/err.h>
@@ -284,33 +285,12 @@ out:
}
#if defined(CONFIG_EFI_CAPSULE_AUTHENTICATE)
int efi_get_public_key_data(void **pkey, efi_uintn_t *pkey_len)
static int efi_get_public_key_data(const void **pkey, efi_uintn_t *pkey_len)
{
const void *fdt_blob = gd->fdt_blob;
const void *blob;
const char *cnode_name = "capsule-key";
const char *snode_name = "signature";
int sig_node;
int len;
const void *blob = __efi_capsule_sig_begin;
const int len = __efi_capsule_sig_end - __efi_capsule_sig_begin;
sig_node = fdt_subnode_offset(fdt_blob, 0, snode_name);
if (sig_node < 0) {
log_err("Unable to get signature node offset\n");
return -FDT_ERR_NOTFOUND;
}
blob = fdt_getprop(fdt_blob, sig_node, cnode_name, &len);
if (!blob || len < 0) {
log_err("Unable to get capsule-key value\n");
*pkey = NULL;
*pkey_len = 0;
return -FDT_ERR_NOTFOUND;
}
*pkey = (void *)blob;
*pkey = blob;
*pkey_len = len;
return 0;
@@ -321,7 +301,8 @@ efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_s
{
u8 *buf;
int ret;
void *fdt_pkey, *pkey;
void *pkey;
const void *stored_pkey;
efi_uintn_t pkey_len;
uint64_t monotonic_count;
struct efi_signature_store *truststore;
@@ -373,7 +354,7 @@ efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_s
goto out;
}
ret = efi_get_public_key_data(&fdt_pkey, &pkey_len);
ret = efi_get_public_key_data(&stored_pkey, &pkey_len);
if (ret < 0)
goto out;
@@ -381,7 +362,7 @@ efi_status_t efi_capsule_authenticate(const void *capsule, efi_uintn_t capsule_s
if (!pkey)
goto out;
memcpy(pkey, fdt_pkey, pkey_len);
memcpy(pkey, stored_pkey, pkey_len);
truststore = efi_build_signature_store(pkey, pkey_len);
if (!truststore)
goto out;

View File

@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* .esl cert for capsule authentication
*
* Copyright (c) 2021, Ilias Apalodimas <ilias.apalodimas@linaro.org>
*/
#include <config.h>
.section .rodata.capsule_key.init,"a"
.balign 16
.global __efi_capsule_sig_begin
__efi_capsule_sig_begin:
.incbin CAPSULE_ESL_PATH
__efi_capsule_sig_end:
.global __efi_capsule_sig_end
.balign 16

View File

@@ -377,35 +377,8 @@ cmd_dtc = mkdir -p $(dir ${dtc-tmp}) ; \
; \
sed "s:$(pre-tmp):$(<):" $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile)
capsule_esl_input_file=$(srctree)/lib/efi_loader/capsule_esl.dtsi.in
capsule_crt_file=$(subst $(quote),,$(CONFIG_EFI_CAPSULE_CRT_FILE))
capsule_esl_dtsi=.capsule_esl.dtsi
quiet_cmd_capsule_esl_gen = CAPSULE_ESL_GEN $@
cmd_capsule_esl_gen = cert-to-efi-sig-list $< $@
$(obj)/capsule_esl_file: $(capsule_crt_file) FORCE
ifeq ($(CONFIG_EFI_CAPSULE_CRT_FILE),"")
$(error "CONFIG_EFI_CAPSULE_CRT_FILE is empty, EFI capsule authentication \
public key must be specified when CONFIG_EFI_CAPSULE_AUTHENTICATE is enabled")
else
$(call cmd,capsule_esl_gen)
endif
quiet_cmd_capsule_dtsi_gen = CAPSULE_DTSI_GEN $@
cmd_capsule_dtsi_gen = \
$(shell sed "s:ESL_BIN_FILE:$(abspath $<):" $(capsule_esl_input_file) > $@)
$(obj)/$(capsule_esl_dtsi): $(obj)/capsule_esl_file FORCE
$(call cmd,capsule_dtsi_gen)
dtsi_include_list_deps := $(addprefix $(u_boot_dtsi_loc),$(subst $(quote),,$(dtsi_include_list)))
ifdef CONFIG_EFI_CAPSULE_AUTHENTICATE
dtsi_include_list += $(capsule_esl_dtsi)
dtsi_include_list_deps += $(obj)/$(capsule_esl_dtsi)
endif
ifneq ($(CHECK_DTBS),)
DT_CHECKER ?= dt-validate
DT_CHECKER_FLAGS ?= $(if $(DT_SCHEMA_FILES),-l $(DT_SCHEMA_FILES),-m)

View File

@@ -49,14 +49,87 @@ static int test_tpm_init(struct unit_test_state *uts, enum tpm_version version)
return 0;
}
static int dm_test_tpm(struct unit_test_state *uts)
static int dm_test_tpm_init(struct unit_test_state *uts)
{
ut_assertok(test_tpm_init(uts, TPM_V1));
ut_assertok(test_tpm_init(uts, TPM_V2));
return 0;
}
DM_TEST(dm_test_tpm, UTF_SCAN_FDT);
DM_TEST(dm_test_tpm_init, UTF_SCAN_FDT);
/* check TPM startup */
static int check_tpm_startup(struct unit_test_state *uts,
enum tpm_version version)
{
struct udevice *dev;
/* check probe success */
ut_assertok(get_tpm_version(version, &dev));
ut_assertok(tpm_init(dev));
ut_assertok(tpm_startup(dev, TPM_ST_CLEAR));
return 0;
}
/* test TPM startup */
static int dm_test_tpm_startup(struct unit_test_state *uts)
{
ut_assertok(check_tpm_startup(uts, TPM_V1));
ut_assertok(check_tpm_startup(uts, TPM_V2));
return 0;
}
DM_TEST(dm_test_tpm_startup, UTF_SCAN_FDT);
static int check_tpm_self_test_full(struct unit_test_state *uts,
enum tpm_version version)
{
struct udevice *dev;
ut_assertok(check_tpm_startup(uts, version));
ut_assertok(get_tpm_version(version, &dev));
ut_assertok(tpm_self_test_full(dev));
return 0;
}
/* Test TPM self-test full */
static int dm_test_tpm_self_test_full(struct unit_test_state *uts)
{
ut_assertok(check_tpm_self_test_full(uts, TPM_V1));
ut_assertok(check_tpm_self_test_full(uts, TPM_V2));
return 0;
}
DM_TEST(dm_test_tpm_self_test_full, UTF_SCAN_FDT);
/* Test TPM self-test continue */
static int test_tpm_self_test_cont(struct unit_test_state *uts,
enum tpm_version version)
{
struct udevice *dev;
/* check probe success */
ut_assertok(get_tpm_version(version, &dev));
ut_assertok(tpm_init(dev));
ut_assertok(tpm_startup(dev, TPM_ST_CLEAR));
ut_assertok(tpm_continue_self_test(dev));
return 0;
}
static int dm_test_tpm_self_test_cont(struct unit_test_state *uts)
{
ut_assertok(test_tpm_self_test_cont(uts, TPM_V1));
ut_assertok(test_tpm_self_test_cont(uts, TPM_V2));
return 0;
}
DM_TEST(dm_test_tpm_self_test_cont, UTF_SCAN_FDT);
/* Test report_state */
static int dm_test_tpm_report_state(struct unit_test_state *uts)

View File

@@ -27,6 +27,16 @@ behavior.
* Setup env__tpm_device_test_skip to True if tests with TPM devices should be
skipped.
Parallel tests
--------------
These tests can be run in parallel on sandbox. In that case any action taken
by one test may be independent of another. For sandbox, care should be taken to
ensure that tests are independent.
Unfortunately, tests cannot be made independent on real hardware, since there is
no way to reset the TPM other than restarting the board. Perhaps that would be
the best approach?
"""
updates = 0
@@ -50,13 +60,8 @@ def force_init(ubman, force=False):
ubman.run_command('tpm2 clear TPM2_RH_PLATFORM')
ubman.run_command('echo --- end of init ---')
def is_sandbox(ubman):
# Array slice removes leading/trailing quotes.
sys_arch = ubman.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1]
return sys_arch == 'sandbox'
@pytest.mark.buildconfigspec('cmd_tpm_v2')
def test_tpm2_init(ubman):
def test_tpm2_autostart(ubman):
"""Init the software stack to use TPMv2 commands."""
skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
if skip_test:
@@ -65,56 +70,6 @@ def test_tpm2_init(ubman):
output = ubman.run_command('echo $?')
assert output.endswith('0')
@pytest.mark.buildconfigspec('cmd_tpm_v2')
def test_tpm2_startup(ubman):
"""Execute a TPM2_Startup command.
Initiate the TPM internal state machine.
"""
skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
if skip_test:
pytest.skip('skip TPM device test')
ubman.run_command('tpm2 startup TPM2_SU_CLEAR')
output = ubman.run_command('echo $?')
assert output.endswith('0')
def tpm2_sandbox_init(ubman):
"""Put sandbox back into a known state so we can run a test
This allows all tests to run in parallel, since no test depends on another.
"""
ubman.restart_uboot()
ubman.run_command('tpm2 autostart')
output = ubman.run_command('echo $?')
assert output.endswith('0')
skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
if skip_test:
pytest.skip('skip TPM device test')
@pytest.mark.buildconfigspec('cmd_tpm_v2')
def test_tpm2_sandbox_self_test_full(ubman):
"""Execute a TPM2_SelfTest (full) command.
Ask the TPM to perform all self tests to also enable full capabilities.
"""
if is_sandbox(ubman):
ubman.restart_uboot()
ubman.run_command('tpm2 autostart')
output = ubman.run_command('echo $?')
assert output.endswith('0')
ubman.run_command('tpm2 startup TPM2_SU_CLEAR')
output = ubman.run_command('echo $?')
assert output.endswith('0')
skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
if skip_test:
pytest.skip('skip TPM device test')
ubman.run_command('tpm2 self_test full')
output = ubman.run_command('echo $?')
assert output.endswith('0')
@pytest.mark.buildconfigspec('cmd_tpm_v2')
def test_tpm2_continue_self_test(ubman):
"""Execute a TPM2_SelfTest (continued) command.
@@ -126,8 +81,6 @@ def test_tpm2_continue_self_test(ubman):
skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
if skip_test:
pytest.skip('skip TPM device test')
if is_sandbox(ubman):
tpm2_sandbox_init(ubman)
ubman.run_command('tpm2 self_test continue')
output = ubman.run_command('echo $?')
assert output.endswith('0')
@@ -144,9 +97,6 @@ def test_tpm2_clear(ubman):
not have a password set, otherwise this test will fail. ENDORSEMENT and
PLATFORM hierarchies are also available.
"""
if is_sandbox(ubman):
tpm2_sandbox_init(ubman)
skip_test = ubman.config.env.get('env__tpm_device_test_skip', False)
if skip_test:
pytest.skip('skip TPM device test')
@@ -167,8 +117,6 @@ def test_tpm2_change_auth(ubman):
Use the LOCKOUT hierarchy for this. ENDORSEMENT and PLATFORM hierarchies are
also available.
"""
if is_sandbox(ubman):
tpm2_sandbox_init(ubman)
force_init(ubman)
ubman.run_command('tpm2 change_auth TPM2_RH_LOCKOUT unicorn')
@@ -193,9 +141,6 @@ def test_tpm2_get_capability(ubman):
There is no expected default values because it would depend on the chip
used. We can still save them in order to check they have changed later.
"""
if is_sandbox(ubman):
tpm2_sandbox_init(ubman)
force_init(ubman)
ram = utils.find_ram_base(ubman)
@@ -217,8 +162,6 @@ def test_tpm2_dam_parameters(ubman):
the authentication, otherwise the lockout will be engaged after the first
failed authentication attempt.
"""
if is_sandbox(ubman):
tpm2_sandbox_init(ubman)
force_init(ubman)
ram = utils.find_ram_base(ubman)
@@ -236,14 +179,12 @@ def test_tpm2_dam_parameters(ubman):
assert 'Property 0x00000211: 0x00000000' in read_cap
@pytest.mark.buildconfigspec('cmd_tpm_v2')
@pytest.mark.notbuildconfigspec('target_chromebook_coral')
def test_tpm2_pcr_read(ubman):
"""Execute a TPM2_PCR_Read command.
Perform a PCR read of the 10th PCR. Must be zero.
"""
if is_sandbox(ubman):
tpm2_sandbox_init(ubman)
force_init(ubman)
ram = utils.find_ram_base(ubman)
@@ -261,6 +202,7 @@ def test_tpm2_pcr_read(ubman):
assert '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' in read_pcr
@pytest.mark.buildconfigspec('cmd_tpm_v2')
@pytest.mark.notbuildconfigspec('target_chromebook_coral')
def test_tpm2_pcr_extend(ubman):
"""Execute a TPM2_PCR_Extend command.
@@ -270,8 +212,6 @@ def test_tpm2_pcr_extend(ubman):
No authentication mechanism is used here, not protecting against packet
replay, yet.
"""
if is_sandbox(ubman):
tpm2_sandbox_init(ubman)
force_init(ubman)
ram = utils.find_ram_base(ubman)