Add CONFIG_TPM_AUTOSTART, which runs the TPM startup and self-test sequence from an EVT_LAST_STAGE_INIT handler. This is the same thing the 'tpm autostart' command does, but it does not depend on a boot script or on the environment.
Today a TPM is only started automatically when either CONFIG_MEASURED_BOOT is used (and then only from bootm) or when booting via EFI with CONFIG_EFI_TCG2_PROTOCOL. A plain booti/bootm therefore leaves the chip uninitialised and the OS has to start it itself, which Linux reports as "A TPM error (256) occurred attempting the self test". Boards have worked around this by putting 'tpm autostart' into their environment, see board/xilinx/zynqmp/zynqmp_kria.env. The startup is done from an event rather than in the uclass probe: nothing probes the TPM on a plain booti/bootm, and the event runs exactly once in U-Boot proper without changing what probing a TPM means for the existing callers. Boards that share one binary between variants with and without a TPM fitted can enable this safely: when no TPM device is found the startup is skipped silently. Other errors are reported as warnings but do not stop the boot. Note that the full self-test adds to the boot time. Signed-off-by: Michael Haener <[email protected]> --- Changes in v3: - Split the tests and the Kria conversion into separate patches Changes in v2: - Move the code to lib/tpm_autostart.c and the option to lib/Kconfig - Stay silent only when no TPM device exists (-ENODEV), warn otherwise - Use u32 and %#x for the tpm_auto_start() result - Mention the boot-time cost of the self-test in the help text - Document the option in doc/usage/measured_boot.rst - Explain in the commit message why an event is used instead of probe doc/usage/measured_boot.rst | 18 ++++++++++++++++++ lib/Kconfig | 19 +++++++++++++++++++ lib/Makefile | 1 + lib/tpm_autostart.c | 37 +++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 lib/tpm_autostart.c diff --git a/doc/usage/measured_boot.rst b/doc/usage/measured_boot.rst index 488dd546f13..d74cfab2594 100644 --- a/doc/usage/measured_boot.rst +++ b/doc/usage/measured_boot.rst @@ -55,3 +55,21 @@ Requirements * The operating system must also be configured to use the memory regions specified in the U-Boot device-tree in order to make use of the event log. + +Starting the TPM without measured boot +-------------------------------------- + +Measured boot starts the TPM as a side effect, but only on the boot paths +listed above. A plain booti/bootm without CONFIG_MEASURED_BOOT, or a boot +which does not go through the EFI subsystem, leaves the chip uninitialised +and the operating system has to start it itself. Some kernels report this as +an error. + +CONFIG_TPM_AUTOSTART runs the same startup and self-test sequence as the +'tpm autostart' command from an EVT_LAST_STAGE_INIT handler, independently of +the boot path, the environment and any boot script. When no TPM device is +found the startup is skipped, so a board which shares one binary between +variants with and without a TPM fitted can enable it safely. + +Note that a full self-test can take a noticeable amount of time on a TPMv2, +which is added to every boot even when the TPM is not used afterwards. diff --git a/lib/Kconfig b/lib/Kconfig index ff7655e1496..33ae8037479 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -565,6 +565,25 @@ config TPM If you want a fully functional TPM enable all hashing algorithms. If you enabled measured boot all hashing algorithms are selected. +config TPM_AUTOSTART + bool "Start the TPM during board initialisation" + depends on TPM + select EVENT + help + Run the TPM startup and self-test sequence from an EVT_LAST_STAGE_INIT + handler, which is the same thing the 'tpm autostart' command does. + Without this the chip is still uninitialised when the OS takes over + and the OS has to start it itself, which some kernels report as an + error. + + Boards that share one binary between variants with and without a TPM + fitted can enable this safely: when no TPM device is found the + startup is skipped silently. + + Note that a full self-test can take a noticeable amount of time on + a TPMv2, so this adds to the boot time of every boot, even when the + TPM is not used afterwards. + config SPL_TPM bool "Trusted Platform Module (TPM) Support in SPL" depends on SPL_DM diff --git a/lib/Makefile b/lib/Makefile index 93a389b10e4..b31861fe4b5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -59,6 +59,7 @@ obj-$(CONFIG_$(PHASE_)OF_LIVE) += of_live.o obj-$(CONFIG_$(PHASE_)TPM) += tpm-common.o ifeq ($(CONFIG_$(PHASE_)TPM),y) obj-$(CONFIG_TPM) += tpm_api.o +obj-$(CONFIG_TPM_AUTOSTART) += tpm_autostart.o obj-$(CONFIG_TPM_V1) += tpm-v1.o obj-$(CONFIG_TPM_V2) += tpm-v2.o obj-$(CONFIG_EFI_TCG2_PROTOCOL) += tpm_tcg2.o diff --git a/lib/tpm_autostart.c b/lib/tpm_autostart.c new file mode 100644 index 00000000000..5b43b2aecbb --- /dev/null +++ b/lib/tpm_autostart.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Start the TPM during init, so that the OS does not have to do it. + * + * Copyright (c) 2026 Siemens AG + */ + +#define LOG_CATEGORY UCLASS_TPM + +#include <dm.h> +#include <errno.h> +#include <event.h> +#include <log.h> +#include <tpm_api.h> + +static int tpm_evt_autostart(void) +{ + struct udevice *dev; + int ret; + u32 rc; + + ret = uclass_first_device_err(UCLASS_TPM, &dev); + if (ret) { + /* a board may share one binary between variants without a TPM */ + if (ret != -ENODEV) + log_warning("Cannot init TPM (err=%dE)\n", ret); + + return 0; + } + + rc = tpm_auto_start(dev); + if (rc) + log_warning("TPM startup failed (rc=%#x)\n", rc); + + return 0; +} +EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, tpm_evt_autostart); -- 2.55.0
