On Fri, 9 Oct 2026 at 00:55, Michael Haener <[email protected]> wrote: > > 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. The startup itself is in tpm_autostart_init(), so that > it can also be called directly, e.g. from tests. > > 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]> > Reviewed-by: Simon Glass <[email protected]> > ---
Reviewed-by: Ilias Apalodimas <[email protected]> > Changes in v4: > - Move the startup into tpm_autostart_init(), so that tests can call it > without sending EVT_LAST_STAGE_INIT > - Add Reviewed-by from Simon > > 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 ++++++++++++++ > include/tpm_api.h | 10 ++++++++ > lib/Kconfig | 19 +++++++++++++++ > lib/Makefile | 1 + > lib/tpm_autostart.c | 48 +++++++++++++++++++++++++++++++++++++ > 5 files changed, 96 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/include/tpm_api.h b/include/tpm_api.h > index 022a8bbaeca..73c0ac6b3e7 100644 > --- a/include/tpm_api.h > +++ b/include/tpm_api.h > @@ -339,4 +339,14 @@ static inline bool tpm_is_v2(struct udevice *dev) > */ > u32 tpm_auto_start(struct udevice *dev); > > +/** > + * tpm_autostart_init() - start up the first TPM, if there is one > + * > + * This is run from EVT_LAST_STAGE_INIT when CONFIG_TPM_AUTOSTART is enabled. > + * Errors are reported as warnings. > + * > + * Return: 0 if OK or if there is no TPM, -ve on error > + */ > +int tpm_autostart_init(void); > + > #endif /* __TPM_API_H */ > 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..ae69f797652 > --- /dev/null > +++ b/lib/tpm_autostart.c > @@ -0,0 +1,48 @@ > +// 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> > + > +int tpm_autostart_init(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) > + return 0; > + > + log_warning("Cannot init TPM (err=%dE)\n", ret); > + return ret; > + } > + > + rc = tpm_auto_start(dev); > + if (rc) { > + log_warning("TPM startup failed (rc=%#x)\n", rc); > + return -EIO; > + } > + > + return 0; > +} > + > +static int tpm_evt_autostart(void) > +{ > + /* errors are reported above and must not stop the boot */ > + tpm_autostart_init(); > + > + return 0; > +} > +EVENT_SPY_SIMPLE(EVT_LAST_STAGE_INIT, tpm_evt_autostart); > -- > 2.55.0 >
