Hi Michael The patches seem reasonable.
Simon wanted to convert the xilinx board as the first user which makes sense. Also can you split this in 3 patches? - Add the new Kconfig option + code - Add the selftests - Tweak the xilinx configs Thanks /Ilias On Tue, 6 Oct 2026 at 12:05, 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. > > 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. > > Enable the option on sandbox and add tests which send the event. > > Signed-off-by: Michael Haener <[email protected]> > --- > 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 > - Enable the option on sandbox and add tests > - Explain in the commit message why an event is used instead of probe > > configs/sandbox_defconfig | 1 + > doc/usage/measured_boot.rst | 18 +++++++++++++++ > lib/Kconfig | 19 ++++++++++++++++ > lib/Makefile | 1 + > lib/tpm_autostart.c | 37 +++++++++++++++++++++++++++++++ > test/dm/tpm.c | 44 +++++++++++++++++++++++++++++++++++++ > 6 files changed, 120 insertions(+) > create mode 100644 lib/tpm_autostart.c > > diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig > index a2708e6fb33..3efbdc6fdf1 100644 > --- a/configs/sandbox_defconfig > +++ b/configs/sandbox_defconfig > @@ -400,6 +400,7 @@ CONFIG_ECDSA=y > CONFIG_ECDSA_VERIFY=y > CONFIG_RSASSA_PSS=y > CONFIG_TPM=y > +CONFIG_TPM_AUTOSTART=y > CONFIG_ERRNO_STR=y > CONFIG_GETOPT=y > CONFIG_TEST_FDTDEC=y > 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); > diff --git a/test/dm/tpm.c b/test/dm/tpm.c > index 87c5c416daa..46e843787f1 100644 > --- a/test/dm/tpm.c > +++ b/test/dm/tpm.c > @@ -5,8 +5,11 @@ > */ > > #include <dm.h> > +#include <event.h> > #include <tpm_api.h> > +#include <dm/device-internal.h> > #include <dm/test.h> > +#include <dm/uclass-internal.h> > #include <test/test.h> > #include <test/ut.h> > > @@ -197,3 +200,44 @@ static int dm_test_tpm_autostart_reinit(struct > unit_test_state *uts) > return 0; > } > DM_TEST(dm_test_tpm_autostart_reinit, UTF_SCAN_FDT); > + > +/* Check that CONFIG_TPM_AUTOSTART starts the TPM from the init event */ > +static int dm_test_tpm_autostart_event(struct unit_test_state *uts) > +{ > + struct udevice *dev; > + char buf[50]; > + > + if (!IS_ENABLED(CONFIG_TPM_AUTOSTART)) > + return 0; > + > + ut_assertok(get_tpm_version(TPM_V2, &dev)); > + > + ut_assert(tpm_report_state(dev, buf, sizeof(buf))); > + ut_asserteq_str("init_done=0", buf); > + > + ut_assertok(event_notify_null(EVT_LAST_STAGE_INIT)); > + > + ut_assert(tpm_report_state(dev, buf, sizeof(buf))); > + ut_asserteq_str("init_done=1", buf); > + > + return 0; > +} > +DM_TEST(dm_test_tpm_autostart_event, UTF_SCAN_FDT); > + > +/* Check that a missing TPM does not make the init event fail */ > +static int dm_test_tpm_autostart_event_missing(struct unit_test_state *uts) > +{ > + struct udevice *dev; > + > + if (!IS_ENABLED(CONFIG_TPM_AUTOSTART)) > + return 0; > + > + while (!uclass_find_first_device(UCLASS_TPM, &dev) && dev) > + ut_assertok(device_unbind(dev)); > + > + ut_asserteq(-ENODEV, uclass_first_device_err(UCLASS_TPM, &dev)); > + ut_assertok(event_notify_null(EVT_LAST_STAGE_INIT)); > + > + return 0; > +} > +DM_TEST(dm_test_tpm_autostart_event_missing, UTF_SCAN_FDT); > > base-commit: 8d7bc8add17bbc69a58dfe0002eea039dd2dc1bc > -- > 2.55.0 >
