diff --git a/docs/TPM2_PCR_MEASUREMENTS.md b/docs/TPM2_PCR_MEASUREMENTS.md
index 7b29069a7e..b05739a8c7 100644
--- a/docs/TPM2_PCR_MEASUREMENTS.md
+++ b/docs/TPM2_PCR_MEASUREMENTS.md
@@ -260,6 +260,15 @@ colon-separated strings, identifying the file system type, UUID, label as well
as the GPT partition entry UUID, entry type UUID and entry label (in UTF-8,
without trailing NUL bytes).
+### PCR 9, NvPCR initialization separator
+
+After completion of `systemd-tpm2-setup.service` (which initializes all NvPCRs
+and measures their initial state) at arly boot the `systemd-pcrnvdone.service`
+service will measure a separator event into PCR 9, isolating the early-boot
+NvPCR initializations from any later additions.
+
+→ **Measured hash** covers the string `nvpcr-separator`.
+
## PCR/NvPCR Measurements Made by `systemd-cryptsetup` (Userspace)
### PCR 15, volume key
diff --git a/man/rules/meson.build b/man/rules/meson.build
index 26eddb7791..6ffc088453 100644
--- a/man/rules/meson.build
+++ b/man/rules/meson.build
@@ -1106,6 +1106,7 @@ manpages = [
'systemd-pcrfs-root.service',
'systemd-pcrfs@.service',
'systemd-pcrmachine.service',
+ 'systemd-pcrnvdone.service',
'systemd-pcrphase-initrd.service',
'systemd-pcrphase-sysinit.service'],
'ENABLE_BOOTLOADER HAVE_OPENSSL HAVE_TPM2'],
diff --git a/man/systemd-pcrphase.service.xml b/man/systemd-pcrphase.service.xml
index 7832e10f85..1d543fe403 100644
--- a/man/systemd-pcrphase.service.xml
+++ b/man/systemd-pcrphase.service.xml
@@ -24,6 +24,7 @@
systemd-pcrproduct.service
systemd-pcrfs-root.service
systemd-pcrfs@.service
+ systemd-pcrnvdone.service
systemd-pcrextend
Measure boot phases, machine ID, product UUID and file system identity into TPM PCRs and NvPCRs
@@ -35,6 +36,7 @@
systemd-pcrmachine.service
systemd-pcrfs-root.service
systemd-pcrfs@.service
+ systemd-pcrnvdone.service
/usr/lib/systemd/systemd-pcrextend STRING
@@ -54,6 +56,9 @@
product UUID (as provided by one of SMBIOS, Devicetree, …) into a NvPCR named
hardware.
+ systemd-pcrnvdone.service is a system service that measures a separator event
+ into PCR 9 once all NvPCRs have completed initialization.
+
systemd-pcrfs-root.service and systemd-pcrfs@.service are
services that measure file system identity information (i.e. mount point, file system type, label and
UUID, partition label and UUID) into PCR 15. systemd-pcrfs-root.service does so for
diff --git a/src/shared/tpm2-util.c b/src/shared/tpm2-util.c
index ad61fd1d8b..1603b5f316 100644
--- a/src/shared/tpm2-util.c
+++ b/src/shared/tpm2-util.c
@@ -6420,13 +6420,14 @@ static int json_dispatch_tpm2_algorithm(const char *name, sd_json_variant *varia
}
static const char* tpm2_userspace_event_type_table[_TPM2_USERSPACE_EVENT_TYPE_MAX] = {
- [TPM2_EVENT_PHASE] = "phase",
- [TPM2_EVENT_FILESYSTEM] = "filesystem",
- [TPM2_EVENT_VOLUME_KEY] = "volume-key",
- [TPM2_EVENT_MACHINE_ID] = "machine-id",
- [TPM2_EVENT_PRODUCT_ID] = "product-id",
- [TPM2_EVENT_KEYSLOT] = "keyslot",
- [TPM2_EVENT_NVPCR_INIT] = "nvpcr-init",
+ [TPM2_EVENT_PHASE] = "phase",
+ [TPM2_EVENT_FILESYSTEM] = "filesystem",
+ [TPM2_EVENT_VOLUME_KEY] = "volume-key",
+ [TPM2_EVENT_MACHINE_ID] = "machine-id",
+ [TPM2_EVENT_PRODUCT_ID] = "product-id",
+ [TPM2_EVENT_KEYSLOT] = "keyslot",
+ [TPM2_EVENT_NVPCR_INIT] = "nvpcr-init",
+ [TPM2_EVENT_NVPCR_SEPARATOR] = "nvpcr-separator",
};
DEFINE_STRING_TABLE_LOOKUP(tpm2_userspace_event_type, Tpm2UserspaceEventType);
diff --git a/src/shared/tpm2-util.h b/src/shared/tpm2-util.h
index 8dfe87af07..59b7ed9984 100644
--- a/src/shared/tpm2-util.h
+++ b/src/shared/tpm2-util.h
@@ -145,6 +145,7 @@ typedef enum Tpm2UserspaceEventType {
TPM2_EVENT_PRODUCT_ID,
TPM2_EVENT_KEYSLOT,
TPM2_EVENT_NVPCR_INIT,
+ TPM2_EVENT_NVPCR_SEPARATOR,
_TPM2_USERSPACE_EVENT_TYPE_MAX,
_TPM2_USERSPACE_EVENT_TYPE_INVALID = -EINVAL,
} Tpm2UserspaceEventType;
diff --git a/units/meson.build b/units/meson.build
index bd788f6d0b..8e5b645f91 100644
--- a/units/meson.build
+++ b/units/meson.build
@@ -581,6 +581,11 @@ units = [
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
'symlinks' : ['sysinit.target.wants/'],
},
+ {
+ 'file' : 'systemd-pcrnvdone.service.in',
+ 'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
+ 'symlinks' : ['sysinit.target.wants/'],
+ },
{
'file' : 'systemd-tpm2-clear.service.in',
'conditions' : ['ENABLE_BOOTLOADER', 'HAVE_OPENSSL', 'HAVE_TPM2'],
diff --git a/units/systemd-pcrnvdone.service.in b/units/systemd-pcrnvdone.service.in
new file mode 100644
index 0000000000..e0dd9a8820
--- /dev/null
+++ b/units/systemd-pcrnvdone.service.in
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: LGPL-2.1-or-later
+#
+# This file is part of systemd.
+#
+# systemd is free software; you can redistribute it and/or modify it
+# under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation; either version 2.1 of the License, or
+# (at your option) any later version.
+
+[Unit]
+Description=TPM PCR NvPCR Initialization Separator
+Documentation=man:systemd-pcrnvdone.service(8)
+DefaultDependencies=no
+Conflicts=shutdown.target
+After=systemd-tpm2-setup-early.service systemd-tpm2-setup.service
+Before=sysinit.target shutdown.target
+ConditionSecurity=measured-uki
+ConditionPathExists=!/etc/initrd-release
+FailureAction=reboot-force
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart={{LIBEXECDIR}}/systemd-pcrextend --graceful --pcr=kernel-initrd --event-type=nvpcr-separator nvpcr-separator