Linux Integrity Measurement development
 help / color / mirror / Atom feed
* [PATCH v5 11/14] module: update timestamp of modules.order after modules are built
From: Thomas Weißschuh @ 2026-05-05  9:05 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Nathan Chancellor,
	Nicolas Schier, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Bouchinet, Xiu Jianfeng, Christophe Leroy
  Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi, Eric Biggers,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, debian-kernel, Thomas Weißschuh
In-Reply-To: <20260505-module-hashes-v5-0-e174a5a49fce@weissschuh.net>

Make sure that modules.order is always newer than the module .ko files.
Other rules can then use modules.order as a prerequisite and rerun if
any module is (re-)built.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 scripts/Makefile.modfinal | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/scripts/Makefile.modfinal b/scripts/Makefile.modfinal
index b09040ccddd2..44a382689a5a 100644
--- a/scripts/Makefile.modfinal
+++ b/scripts/Makefile.modfinal
@@ -66,6 +66,13 @@ endif
 
 targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) .module-common.o
 
+# Update modules.order when a module is (re-)built.
+# Allow using it as target dependency.
+targets += modules.order
+__modfinal: modules.order
+modules.order: $(modules:%.o=%.ko)
+	@touch $@
+
 # Add FORCE to the prerequisites of a target to force it to be always rebuilt.
 # ---------------------------------------------------------------------------
 

-- 
2.54.0


^ permalink raw reply related

* [PATCH v5 12/14] module: Introduce hash-based integrity checking
From: Thomas Weißschuh @ 2026-05-05  9:05 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Nathan Chancellor,
	Nicolas Schier, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Bouchinet, Xiu Jianfeng, Christophe Leroy
  Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi, Eric Biggers,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, debian-kernel, Thomas Weißschuh
In-Reply-To: <20260505-module-hashes-v5-0-e174a5a49fce@weissschuh.net>

The current signature-based module integrity checking has some drawbacks
in combination with reproducible builds. Either the module signing key
is generated at build time, which makes the build unreproducible, or a
static signing key is used, which precludes rebuilds by third parties
and makes the whole build and packaging process much more complicated.

The goal is to reach bit-for-bit reproducibility. Excluding certain
parts of the build output from the reproducibility analysis would be
error-prone and force each downstream consumer to introduce new tooling.

Introduce a new mechanism to ensure only well-known modules are loaded
by embedding a merkle tree root of all modules built as part of the full
kernel build into vmlinux.

Out-of-tree modules can be validated as before through signatures.

Normally the .ko module files depend on a fully built vmlinux to be
available for modpost validation and BTF generation. With
CONFIG_MODULE_HASHES, vmlinux now depends on the modules
to build a merkle tree. This introduces a dependency cycle which is
impossible to satisfy. Work around this by building the modules during
link-vmlinux.sh, after vmlinux is complete enough for modpost and BTF
but before the final module hashes are

The PKCS7 format which is used for regular module signatures can not
represent Merkle proofs, so a new kind of module signature is
introduced. As this signature type is only ever used for builtin
modules, no compatibility issues can arise.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 .gitignore                                   |   1 +
 Documentation/kbuild/reproducible-builds.rst |   5 +-
 Makefile                                     |   7 +-
 include/asm-generic/vmlinux.lds.h            |  11 +
 include/linux/module_hashes.h                |  29 ++
 include/uapi/linux/module_signature.h        |   1 +
 kernel/module/Kconfig                        |  21 +-
 kernel/module/Makefile                       |   1 +
 kernel/module/auth.c                         |   6 +
 kernel/module/hashes.c                       |  95 ++++++
 kernel/module/hashes_root.c                  |   6 +
 kernel/module/internal.h                     |   1 +
 scripts/.gitignore                           |   1 +
 scripts/Makefile                             |   4 +
 scripts/Makefile.modinst                     |  11 +
 scripts/Makefile.vmlinux                     |  32 +++
 scripts/include/xalloc.h                     |  29 ++
 scripts/link-vmlinux.sh                      |   3 +-
 scripts/modules-merkle-tree.c                | 416 +++++++++++++++++++++++++++
 security/lockdown/Kconfig                    |   2 +-
 tools/include/uapi/linux/module_signature.h  |   1 +
 21 files changed, 677 insertions(+), 6 deletions(-)

diff --git a/.gitignore b/.gitignore
index 3044b9590f05..78cf799401e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -36,6 +36,7 @@
 *.lz4
 *.lzma
 *.lzo
+*.merkle
 *.mod
 *.mod.c
 *.o
diff --git a/Documentation/kbuild/reproducible-builds.rst b/Documentation/kbuild/reproducible-builds.rst
index bc1eb82211df..b15019678aae 100644
--- a/Documentation/kbuild/reproducible-builds.rst
+++ b/Documentation/kbuild/reproducible-builds.rst
@@ -84,7 +84,10 @@ generate a different temporary key for each build, resulting in the
 modules being unreproducible.  However, including a signing key with
 your source would presumably defeat the purpose of signing modules.
 
-One approach to this is to divide up the build process so that the
+Instead ``CONFIG_MODULE_HASHES`` can be used to embed a static list
+of valid modules to load.
+
+Another approach to this is to divide up the build process so that the
 unreproducible parts can be treated as sources:
 
 1. Generate a persistent signing key.  Add the certificate for the key
diff --git a/Makefile b/Makefile
index e27c91ea56fc..def4a2413c43 100644
--- a/Makefile
+++ b/Makefile
@@ -1650,7 +1650,9 @@ ifdef CONFIG_MODULES
 
 # By default, build modules as well
 
+ifndef CONFIG_MODULE_HASHES
 all: modules
+endif
 
 # When we're building modules with modversions, we need to consider
 # the built-in objects during the descend as well, in order to
@@ -1666,8 +1668,10 @@ endif
 # is an exception.
 ifdef CONFIG_DEBUG_INFO_BTF_MODULES
 KBUILD_BUILTIN := y
+ifndef CONFIG_MODULE_HASHES
 modules: vmlinux
 endif
+endif
 
 modules: modules_prepare
 
@@ -2068,7 +2072,7 @@ modules.order: $(build-dir)
 # KBUILD_MODPOST_NOFINAL can be set to skip the final link of modules.
 # This is solely useful to speed up test compiles.
 modules: modpost
-ifneq ($(KBUILD_MODPOST_NOFINAL),1)
+ifneq ($(CONFIG_MODULE_HASHES)|$(KBUILD_MODPOST_NOFINAL),|1)
 	$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modfinal
 endif
 
@@ -2162,6 +2166,7 @@ clean: $(clean-dirs)
 		-o -name '*.c.[012]*.*' \
 		-o -name '*.ll' \
 		-o -name '*.gcno' \
+		-o -name '*.merkle' \
 		\) -type f -print \
 		-o -name '.tmp_*' -print \
 		| xargs rm -rf
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 60c8c22fd3e4..661881e5ef96 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -508,6 +508,8 @@
 									\
 	PRINTK_INDEX							\
 									\
+	MODULE_HASHES							\
+									\
 	/* Kernel symbol table */					\
 	__ksymtab         : AT(ADDR(__ksymtab) - LOAD_OFFSET) {		\
 		__start___ksymtab = .;					\
@@ -913,6 +915,15 @@
 #define PRINTK_INDEX
 #endif
 
+#ifdef CONFIG_MODULE_HASHES
+#define MODULE_HASHES							\
+	.module_hashes : AT(ADDR(.module_hashes) - LOAD_OFFSET) {	\
+		KEEP(*(SORT(.module_hashes)))				\
+	}
+#else
+#define MODULE_HASHES
+#endif
+
 /*
  * Discard .note.GNU-stack, which is emitted as PROGBITS by the compiler.
  * Otherwise, the type of .notes section would become PROGBITS instead of NOTES.
diff --git a/include/linux/module_hashes.h b/include/linux/module_hashes.h
new file mode 100644
index 000000000000..53b34fa12f2d
--- /dev/null
+++ b/include/linux/module_hashes.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#ifndef _LINUX_MODULE_HASHES_H
+#define _LINUX_MODULE_HASHES_H
+
+#include <linux/compiler_attributes.h>
+#include <linux/types.h>
+#include <crypto/sha2.h>
+
+#define __module_hashes_section __section(".module_hashes")
+#define MODULE_HASHES_HASH_SIZE SHA256_DIGEST_SIZE
+
+struct module_hash {
+	u8 h[MODULE_HASHES_HASH_SIZE];
+};
+
+struct module_hashes_proof {
+	__be32 pos;
+	struct module_hash hash_sigs[];
+} __packed;
+
+struct module_hashes_root {
+	u32 levels;
+	struct module_hash hash;
+};
+
+extern const struct module_hashes_root module_hashes_root;
+
+#endif /* _LINUX_MODULE_HASHES_H */
diff --git a/include/uapi/linux/module_signature.h b/include/uapi/linux/module_signature.h
index 634c9f1c8fc2..78e206996eed 100644
--- a/include/uapi/linux/module_signature.h
+++ b/include/uapi/linux/module_signature.h
@@ -16,6 +16,7 @@
 
 enum module_signature_type {
 	MODULE_SIGNATURE_TYPE_PKCS7 = 2,	/* Signature in PKCS#7 message */
+	MODULE_SIGNATURE_TYPE_MERKLE = 3,	/* Merkle proof for modules, opaque structure */
 };
 
 /*
diff --git a/kernel/module/Kconfig b/kernel/module/Kconfig
index 84297da666ff..acbbda58e7c8 100644
--- a/kernel/module/Kconfig
+++ b/kernel/module/Kconfig
@@ -272,7 +272,7 @@ config MODULE_SIG
 	  inclusion into an initramfs that wants the module size reduced.
 
 config MODULE_AUTH
-	def_bool MODULE_SIG
+	def_bool MODULE_SIG || MODULE_HASHES
 
 config MODULE_SIG_FORCE
 	bool "Require modules to be validly signed"
@@ -291,7 +291,7 @@ config MODULE_SIG_ALL
 	  modules must be signed manually, using the scripts/sign-file tool.
 
 comment "Do not forget to sign required modules with scripts/sign-file"
-	depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL
+	depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL && !MODULE_HASHES
 
 choice
 	prompt "Hash algorithm to sign modules"
@@ -406,6 +406,23 @@ config MODULE_DECOMPRESS
 
 endif # MODULE_COMPRESS
 
+config MODULE_HASHES
+	bool "Hash-based module authentication"
+	depends on !MODULE_SIG_ALL
+	depends on !IMA_APPRAISE_MODSIG
+	select MODULE_SIG_FORMAT
+	select CRYPTO_LIB_SHA256
+	help
+	  Validate modules by their hashes.
+	  Only modules built together with the main kernel image can be
+	  validated that way.
+
+	  This is a reproducible-build compatible alternative to a build-time
+	  generated module keyring, as enabled by
+	  CONFIG_MODULE_SIG_KEY=certs/signing_key.pem.
+
+	  Also see the warning in MODULE_SIG about stripping modules.
+
 config MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS
 	bool "Allow loading of modules with missing namespace imports"
 	help
diff --git a/kernel/module/Makefile b/kernel/module/Makefile
index c7200e293d04..da9420f140e9 100644
--- a/kernel/module/Makefile
+++ b/kernel/module/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_KGDB_KDB) += kdb.o
 obj-$(CONFIG_MODVERSIONS) += version.o
 obj-$(CONFIG_MODULE_UNLOAD_TAINT_TRACKING) += tracking.o
 obj-$(CONFIG_MODULE_STATS) += stats.o
+obj-$(CONFIG_MODULE_HASHES) += hashes.o hashes_root.o
diff --git a/kernel/module/auth.c b/kernel/module/auth.c
index 2ee512d26790..cf3fe3f8bd89 100644
--- a/kernel/module/auth.c
+++ b/kernel/module/auth.c
@@ -42,6 +42,9 @@ static __always_inline bool mod_sig_type_valid(enum module_signature_type id_typ
 	if (id_type == MODULE_SIGNATURE_TYPE_PKCS7 && IS_ENABLED(CONFIG_MODULE_SIG))
 		return true;
 
+	if (id_type == MODULE_SIGNATURE_TYPE_MERKLE && IS_ENABLED(CONFIG_MODULE_HASHES))
+		return true;
+
 	return false;
 }
 
@@ -72,6 +75,9 @@ static int mod_verify_sig(const void *mod, struct load_info *info)
 	if (ms.id_type == MODULE_SIGNATURE_TYPE_PKCS7 && IS_ENABLED(CONFIG_MODULE_SIG))
 		return module_sig_check(mod, modlen, mod + modlen, sig_len);
 
+	if (ms.id_type == MODULE_SIGNATURE_TYPE_MERKLE && IS_ENABLED(CONFIG_MODULE_HASHES))
+		return module_hash_check(mod, modlen, mod + modlen, sig_len);
+
 	return 0;
 }
 
diff --git a/kernel/module/hashes.c b/kernel/module/hashes.c
new file mode 100644
index 000000000000..3d3cf0366f75
--- /dev/null
+++ b/kernel/module/hashes.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Module hash-based integrity checker
+ *
+ * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
+ * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
+ *
+ * The structure of the Merkle tree is documented in scripts/modules-merkle-tree.c.
+ */
+
+#define pr_fmt(fmt) "module/hash: " fmt
+
+#include <linux/module_hashes.h>
+#include <linux/module.h>
+#include <linux/unaligned.h>
+
+#include <crypto/sha2.h>
+
+#include "internal.h"
+
+static __init __maybe_unused int module_hashes_init(void)
+{
+	pr_debug("root: levels=%u hash=%*phN\n",
+		 module_hashes_root.levels,
+		 (int)sizeof(module_hashes_root.hash), &module_hashes_root.hash);
+
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_MODULE_DEBUG)
+early_initcall(module_hashes_init);
+#endif
+
+static void hash_entry(const struct module_hash *left, const struct module_hash *right,
+		       struct module_hash *out)
+{
+	struct sha256_ctx ctx;
+	u8 magic = 0x02;
+
+	sha256_init(&ctx);
+	sha256_update(&ctx, &magic, sizeof(magic));
+	sha256_update(&ctx, left->h, sizeof(left->h));
+	sha256_update(&ctx, right->h, sizeof(right->h));
+	sha256_final(&ctx, out->h);
+}
+
+static void hash_data(const u8 *d, size_t len, unsigned int pos, struct module_hash *out)
+{
+	struct sha256_ctx ctx;
+	u8 magic = 0x01;
+	__be32 pos_be;
+
+	pos_be = cpu_to_be32(pos);
+
+	sha256_init(&ctx);
+	sha256_update(&ctx, &magic, sizeof(magic));
+	sha256_update(&ctx, (const u8 *)&pos_be, sizeof(pos_be));
+	sha256_update(&ctx, d, len);
+	sha256_final(&ctx, out->h);
+}
+
+static bool module_hashes_verify_proof(u32 pos, const struct module_hash *hash_sigs,
+				       struct module_hash *cur)
+{
+	for (unsigned int i = 0; i < module_hashes_root.levels; i++, pos >>= 1) {
+		if ((pos & 1) == 0)
+			hash_entry(cur, &hash_sigs[i], cur);
+		else
+			hash_entry(&hash_sigs[i], cur, cur);
+	}
+
+	return !memcmp(cur, &module_hashes_root.hash, sizeof(module_hashes_root.hash));
+}
+
+int module_hash_check(const void *mod, size_t mod_len, const void *sig, size_t sig_len)
+{
+	const struct module_hashes_proof *proof;
+	struct module_hash modhash;
+	size_t proof_size;
+	u32 pos;
+
+	proof_size = struct_size(proof, hash_sigs, module_hashes_root.levels);
+
+	if (sig_len != proof_size)
+		return -ENOPKG;
+
+	proof = (const struct module_hashes_proof *)sig;
+	pos = get_unaligned_be32(&proof->pos);
+
+	hash_data(mod, mod_len, pos, &modhash);
+
+	if (!module_hashes_verify_proof(pos, proof->hash_sigs, &modhash))
+		return -ENOKEY;
+
+	return 0;
+}
diff --git a/kernel/module/hashes_root.c b/kernel/module/hashes_root.c
new file mode 100644
index 000000000000..ffb6adfc2193
--- /dev/null
+++ b/kernel/module/hashes_root.c
@@ -0,0 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/module_hashes.h>
+
+/* Blank dummy data. Will be replaced by the read data during the build */
+const struct module_hashes_root module_hashes_root __module_hashes_section = {};
diff --git a/kernel/module/internal.h b/kernel/module/internal.h
index aabe7f8e1af4..259e8ca5cb25 100644
--- a/kernel/module/internal.h
+++ b/kernel/module/internal.h
@@ -336,6 +336,7 @@ void module_mark_ro_after_init(const Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
 			       const char *secstrings);
 
 int module_sig_check(const void *mod, size_t mod_len, const void *sig, size_t sig_len);
+int module_hash_check(const void *mod, size_t mod_len, const void *sig, size_t sig_len);
 
 #ifdef CONFIG_MODULE_AUTH
 int module_auth_check(struct load_info *info, int flags);
diff --git a/scripts/.gitignore b/scripts/.gitignore
index 4215c2208f7e..8dad9b0d3b2d 100644
--- a/scripts/.gitignore
+++ b/scripts/.gitignore
@@ -5,6 +5,7 @@
 /insert-sys-cert
 /kallsyms
 /module.lds
+/modules-merkle-tree
 /recordmcount
 /rustdoc_test_builder
 /rustdoc_test_gen
diff --git a/scripts/Makefile b/scripts/Makefile
index c983e09be78c..b6291595d9e8 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -11,6 +11,7 @@ hostprogs-always-y					+= sign-file
 hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE)	+= insert-sys-cert
 hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS)		+= rustdoc_test_builder
 hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS)		+= rustdoc_test_gen
+hostprogs-always-$(CONFIG_MODULE_HASHES)		+= modules-merkle-tree
 hostprogs-always-$(CONFIG_TRACEPOINTS)			+= tracepoint-update
 
 sorttable-objs := sorttable.o elf-parse.o
@@ -37,6 +38,9 @@ HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
 HOSTCFLAGS_sign-file.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
 HOSTCFLAGS_sign-file.o += -I$(srctree)/tools/include/uapi/
 HOSTLDLIBS_sign-file = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
+HOSTCFLAGS_modules-merkle-tree.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
+HOSTCFLAGS_modules-merkle-tree.o += -I$(srctree)/tools/include/uapi/
+HOSTLDLIBS_modules-merkle-tree = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
 
 ifdef CONFIG_UNWINDER_ORC
 ifeq ($(ARCH),x86_64)
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index 9ba45e5b32b1..68708a039a62 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -79,6 +79,12 @@ quiet_cmd_install = INSTALL $@
 # as the options to the strip command.
 ifdef INSTALL_MOD_STRIP
 
+ifdef CONFIG_MODULE_HASHES
+ifeq ($(KBUILD_EXTMOD),)
+$(error CONFIG_MODULE_HASHES and INSTALL_MOD_STRIP are mutually exclusive)
+endif
+endif
+
 ifeq ($(INSTALL_MOD_STRIP),1)
 strip-option := --strip-debug
 else
@@ -116,6 +122,11 @@ quiet_cmd_sign :=
       cmd_sign := :
 endif
 
+ifeq ($(KBUILD_EXTMOD)|$(CONFIG_MODULE_HASHES),|y)
+quiet_cmd_sign = MERKLE [M] $@
+      cmd_sign = cat $(objtree)/$*.merkle >> $@
+endif
+
 # Create necessary directories
 $(foreach dir, $(sort $(dir $(install-y))), $(shell mkdir -p $(dir)))
 
diff --git a/scripts/Makefile.vmlinux b/scripts/Makefile.vmlinux
index 6cc661e5292b..a0332c06bde1 100644
--- a/scripts/Makefile.vmlinux
+++ b/scripts/Makefile.vmlinux
@@ -78,6 +78,33 @@ ifdef CONFIG_BUILDTIME_TABLE_SORT
 vmlinux.unstripped: scripts/sorttable
 endif
 
+ifdef CONFIG_MODULE_HASHES
+targets += .tmp_module_hashes.c
+
+modules.order: vmlinux.unstripped FORCE
+	$(Q)echo "  MAKE    modules"
+	$(Q)$(MAKE) -f $(srctree)/Makefile modules
+
+quiet_cmd_modules_merkle_tree = MERKLE  $@
+      cmd_modules_merkle_tree = $< $@ .ko
+
+targets += .tmp_module_hashes.c
+.tmp_module_hashes.c: $(objtree)/scripts/modules-merkle-tree modules.order FORCE
+	$(call if_changed,modules_merkle_tree)
+
+targets += .tmp_module_hashes.o
+.tmp_module_hashes.o: .tmp_module_hashes.c FORCE
+
+quiet_cmd_modules_merkle_tree_root = GEN     $@
+      cmd_modules_merkle_tree_root = $(OBJCOPY) --dump-section .module_hashes=$@ $<
+
+targets += .tmp_module_hashes.bin
+.tmp_module_hashes.bin: .tmp_module_hashes.o FORCE
+	$(call if_changed,modules_merkle_tree_root)
+
+vmlinux: .tmp_module_hashes.bin
+endif
+
 # vmlinux
 # ---------------------------------------------------------------------------
 
@@ -95,6 +122,11 @@ quiet_cmd_objcopy_vmlinux = OBJCOPY $@
       cmd_objcopy_vmlinux = $(OBJCOPY) $(patsubst %,--set-section-flags %=noload,$(remove-section-y)) $< $@; \
                             $(OBJCOPY) $(addprefix --remove-section=,$(remove-section-y)) $(remove-symbols) $@
 
+ifdef CONFIG_MODULE_HASHES
+# Patch module hashes root into vmlinux after modules have been built.
+      cmd_objcopy_vmlinux += ; $(OBJCOPY) --update-section .module_hashes=.tmp_module_hashes.bin $@
+endif
+
 targets += vmlinux
 vmlinux: vmlinux.unstripped FORCE
 	$(call if_changed,objcopy_vmlinux)
diff --git a/scripts/include/xalloc.h b/scripts/include/xalloc.h
index cdadb07d0592..8294bc0b836f 100644
--- a/scripts/include/xalloc.h
+++ b/scripts/include/xalloc.h
@@ -3,6 +3,7 @@
 #ifndef XALLOC_H
 #define XALLOC_H
 
+#include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -50,4 +51,32 @@ static inline char *xstrndup(const char *s, size_t n)
 	return p;
 }
 
+static inline void *xreallocarray(void *oldp, size_t n, size_t size)
+{
+	void *p;
+
+	p = reallocarray(oldp, n, size);
+	if (!p)
+		exit(1);
+
+	return p;
+}
+
+#ifdef _GNU_SOURCE
+static inline char *xasprintf(const char *fmt, ...)
+{
+	va_list ap;
+	char *strp;
+	int ret;
+
+	va_start(ap, fmt);
+	ret = vasprintf(&strp, fmt, ap);
+	va_end(ap);
+	if (ret == -1)
+		exit(1);
+
+	return strp;
+}
+#endif /* _GNU_SOURCE */
+
 #endif /* XALLOC_H */
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index f99e196abeea..0edec5ee34dc 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -103,7 +103,7 @@ vmlinux_link()
 	${ld} ${ldflags} -o ${output}					\
 		${wl}--whole-archive ${objs} ${wl}--no-whole-archive	\
 		${wl}--start-group ${libs} ${wl}--end-group		\
-		${kallsymso} ${btf_vmlinux_bin_o} ${arch_vmlinux_o} ${ldlibs}
+		${kallsymso} ${btf_vmlinux_bin_o} ${module_hashes_o} ${arch_vmlinux_o} ${ldlibs}
 }
 
 # Create ${2}.o file with all symbols from the ${1} object file
@@ -183,6 +183,7 @@ fi
 btf_vmlinux_bin_o=
 btfids_vmlinux=
 kallsymso=
+module_hashes_o=
 strip_debug=
 generate_map=
 
diff --git a/scripts/modules-merkle-tree.c b/scripts/modules-merkle-tree.c
new file mode 100644
index 000000000000..10e3455d5d7a
--- /dev/null
+++ b/scripts/modules-merkle-tree.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Compute hashes for modules files and build a merkle tree.
+ *
+ * Copyright (C) 2025 Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
+ * Copyright (C) 2025 Thomas Weißschuh <linux@weissschuh.net>
+ *
+ * Structure of the Merkle tree:
+ *
+ * The full built modules are leaf nodes. They are hashed pairwise in the order
+ * of modules.order to create internal nodes. These in turn are also hashed
+ * pairwise to create the next higher level of internal nodes. This is repeated
+ * up to a single root node. In case of an uneven amount of node on a level, the
+ * last node is paired with itself.
+ *
+ * The single root node can then be embedded into vmlinux to validate all modules.
+ */
+
+#define _GNU_SOURCE 1
+#include <endian.h>
+#include <err.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+#include <openssl/evp.h>
+#include <openssl/err.h>
+
+#include "ssl-common.h"
+
+#include <linux/module_signature.h>
+#include <xalloc.h>
+
+static int hash_size;
+static EVP_MD_CTX *ctx;
+
+struct hash {
+	uint8_t h[32]; /* For sha256 */
+};
+
+struct file_entry {
+	char *name;
+	unsigned int pos;
+	struct hash hash;
+};
+
+static struct file_entry *fh_list;
+static size_t num_files;
+
+struct mtree {
+	struct hash **level_hashes;
+	unsigned int *entries;
+	unsigned int num_levels;
+};
+
+static unsigned int log2_roundup(uint32_t val)
+{
+	if (val <= 1)
+		return 1;
+	return 32 - __builtin_clz(val - 1);
+}
+
+static void hash_data(unsigned int pos, unsigned char *data, size_t size, struct hash *ret_hash)
+{
+	uint8_t magic = 0x01; /* domain separation prefix */
+	uint32_t pos_be;
+
+	pos_be = htobe32(pos);
+
+	ERR(EVP_DigestInit_ex(ctx, NULL, NULL) != 1, "EVP_DigestInit_ex()");
+	ERR(EVP_DigestUpdate(ctx, &magic, sizeof(magic)) != 1, "EVP_DigestUpdate(magic)");
+	ERR(EVP_DigestUpdate(ctx, &pos_be, sizeof(pos_be)) != 1, "EVP_DigestUpdate(pos)");
+	ERR(EVP_DigestUpdate(ctx, data, size) != 1, "EVP_DigestUpdate(data)");
+	ERR(EVP_DigestFinal_ex(ctx, ret_hash->h, NULL) != 1, "EVP_DigestFinal_ex()");
+}
+
+static void hash_entry(const struct hash *left, const struct hash *right, struct hash *ret_hash)
+{
+	uint8_t magic = 0x02; /* domain separation prefix */
+
+	ERR(EVP_DigestInit_ex(ctx, NULL, NULL) != 1, "EVP_DigestInit_ex()");
+	ERR(EVP_DigestUpdate(ctx, &magic, sizeof(magic)) != 1, "EVP_DigestUpdate(magic)");
+	ERR(EVP_DigestUpdate(ctx, left, hash_size) != 1, "EVP_DigestUpdate(left)");
+	ERR(EVP_DigestUpdate(ctx, right, hash_size) != 1, "EVP_DigestUpdate(right)");
+	ERR(EVP_DigestFinal_ex(ctx, ret_hash->h, NULL) != 1, "EVP_DigestFinal_ex()");
+}
+
+static void hash_file(struct file_entry *fe)
+{
+	unsigned char *mem;
+	struct stat sb;
+	int fd, ret;
+
+	fd = open(fe->name, O_RDONLY);
+	if (fd < 0)
+		err(1, "Failed to open %s", fe->name);
+
+	ret = fstat(fd, &sb);
+	if (ret)
+		err(1, "Failed to stat %s", fe->name);
+
+	mem = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
+	if (mem == MAP_FAILED)
+		err(1, "Failed to mmap %s", fe->name);
+
+	hash_data(fe->pos, mem, sb.st_size, &fe->hash);
+
+	munmap(mem, sb.st_size);
+	close(fd);
+}
+
+static struct mtree *build_merkle(struct file_entry *files, size_t num_files)
+{
+	unsigned int num_cur_le, num_prev_le;
+	struct mtree *mt;
+
+	if (!num_files)
+		return NULL;
+
+	mt = xmalloc(sizeof(*mt));
+	mt->num_levels = log2_roundup(num_files);
+
+	mt->level_hashes = xcalloc(sizeof(*mt->level_hashes), mt->num_levels);
+
+	mt->entries = xcalloc(sizeof(*mt->entries), mt->num_levels);
+	num_cur_le = (num_files + 1) / 2;
+	mt->entries[0] = num_cur_le;
+	mt->level_hashes[0] = xcalloc(sizeof(**mt->level_hashes), num_cur_le);
+
+	/* First level of pairs */
+	for (size_t i = 0; i < num_files; i += 2) {
+		/* Hash the pair, or the last file with itself if it's odd. */
+		const struct hash *right = i + 1 < num_files ? &files[i + 1].hash : &files[i].hash;
+
+		hash_entry(&files[i].hash, right, &mt->level_hashes[0][i / 2]);
+	}
+
+	for (unsigned int i = 1; i < mt->num_levels; i++) {
+		num_prev_le = num_cur_le;
+
+		num_cur_le = (num_prev_le + 1) / 2;
+		mt->entries[i] = num_cur_le;
+		mt->level_hashes[i] = xcalloc(sizeof(**mt->level_hashes), num_cur_le);
+
+		for (unsigned int n = 0; n < num_prev_le; n += 2) {
+			/* Hash the pair, or the last with itself if it's odd. */
+			const struct hash *right = n + 1 < num_prev_le ?
+						   &mt->level_hashes[i - 1][n + 1] :
+						   &mt->level_hashes[i - 1][n];
+			hash_entry(&mt->level_hashes[i - 1][n], right,
+				   &mt->level_hashes[i][n / 2]);
+		}
+	}
+
+	/* FIXME assert single hash in root */
+
+	return mt;
+}
+
+static void free_mtree(struct mtree *mt)
+{
+	if (!mt)
+		return;
+
+	for (unsigned int i = 0; i < mt->num_levels; i++)
+		free(mt->level_hashes[i]);
+
+	free(mt->level_hashes);
+	free(mt->entries);
+	free(mt);
+}
+
+static void write_be_int(int fd, unsigned int v)
+{
+	unsigned int be_val = htobe32(v);
+
+	if (write(fd, &be_val, sizeof(be_val)) != sizeof(be_val))
+		err(1, "Failed writing to file");
+}
+
+static void write_hash(int fd, const struct hash *hash)
+{
+	if (write(fd, hash->h, hash_size) != hash_size)
+		err(1, "Failed writing to file");
+}
+
+static void build_proof(struct mtree *mt, unsigned int n, int fd)
+{
+	struct file_entry *fe, *fe_sib;
+
+	fe = &fh_list[n];
+
+	if ((n & 1) == 0) {
+		/* No pair, hash with itself */
+		if (n + 1 == num_files)
+			fe_sib = fe;
+		else
+			fe_sib = &fh_list[n + 1];
+	} else {
+		fe_sib = &fh_list[n - 1];
+	}
+	/* First comes the node position into the file */
+	write_be_int(fd, n);
+
+	/* Next is the sibling hash, followed by hashes in the tree */
+	write_hash(fd, &fe_sib->hash);
+
+	for (unsigned int i = 0; i < mt->num_levels - 1; i++) {
+		n >>= 1;
+		if ((n & 1) == 0) {
+			const struct hash *h;
+
+			/* No pair, hash with itself */
+			if (n + 1 == mt->entries[i])
+				h = &mt->level_hashes[i][n];
+			else
+				h = &mt->level_hashes[i][n + 1];
+
+			write_hash(fd, h);
+		} else {
+			write_hash(fd, &mt->level_hashes[i][n - 1]);
+		}
+	}
+}
+
+static void append_module_signature_magic(int fd, unsigned int sig_len)
+{
+	const struct module_signature sig_info = {
+		.id_type	= MODULE_SIGNATURE_TYPE_MERKLE,
+		.sig_len	= htobe32(sig_len),
+	};
+	const size_t sig_str_len = sizeof(MODULE_SIGNATURE_MARKER) - 1;
+	const char *sig_str = MODULE_SIGNATURE_MARKER;
+
+	if (write(fd, &sig_info, sizeof(sig_info)) != sizeof(sig_info))
+		err(1, "write(sig_info) failed");
+
+	if (write(fd, sig_str, sig_str_len) != sig_str_len)
+		err(1, "write(magic_number) failed");
+}
+
+static void write_merkle_root(struct mtree *mt, const char *filename)
+{
+	unsigned int num_levels;
+	struct hash *h;
+	FILE *f;
+
+	if (mt) {
+		num_levels = mt->num_levels;
+		h = &mt->level_hashes[mt->num_levels - 1][0];
+	} else {
+		num_levels = 0;
+		h = xcalloc(1, hash_size);
+	}
+
+	f = fopen(filename, "w");
+	if (!f)
+		err(1, "Failed to create %s", filename);
+
+	fprintf(f, "#include <linux/module_hashes.h>\n\n");
+	fprintf(f, "const struct\n");
+	fprintf(f, "module_hashes_root module_hashes_root __module_hashes_section = {\n");
+
+	fprintf(f, "\t.levels = %u,\n", num_levels);
+	fprintf(f, "\t.hash = {{");
+	for (unsigned int i = 0; i < hash_size; i++) {
+		char *space = "";
+
+		if (!(i % 8))
+			fprintf(f, "\n\t\t");
+
+		if ((i + 1) % 8)
+			space = " ";
+
+		fprintf(f, "0x%02x,%s", h->h[i], space);
+	}
+	fprintf(f, "\n\t}},");
+
+	fprintf(f, "\n};\n");
+
+	if (fclose(f))
+		err(1, "Failed to write %s", filename);
+
+	if (!mt)
+		free(h);
+}
+
+static char *xstrdup_replace_suffix(const char *str, const char *old_suffix, const char *new_suffix)
+{
+	size_t str_len, old_suffix_len, base_len;
+
+	str_len = strlen(str);
+	old_suffix_len = strlen(old_suffix);
+	base_len = str_len - old_suffix_len;
+
+	if (old_suffix_len > str_len || memcmp(str + base_len, old_suffix, old_suffix_len) != 0)
+		errx(1, "'%s' does not end in '%s'", str, old_suffix);
+
+	return xasprintf("%.*s%s", (int)base_len, str, new_suffix);
+}
+
+static void trim_newline(char *line)
+{
+	size_t len;
+
+	if (!line)
+		return;
+
+	len = strlen(line);
+	if (!len)
+		return;
+
+	if (line[len - 1] == '\n')
+		line[len - 1] = '\0';
+}
+
+static void read_modules_order(const char *fname, const char *suffix)
+{
+	char line[PATH_MAX];
+	FILE *in;
+
+	in = fopen(fname, "r");
+	if (!in)
+		err(1, "Failed to open %s", fname);
+
+	while (fgets(line, PATH_MAX, in)) {
+		struct file_entry *entry;
+
+		trim_newline(line);
+
+		fh_list = xreallocarray(fh_list, num_files + 1, sizeof(*fh_list));
+		entry = &fh_list[num_files];
+
+		entry->pos = num_files;
+		entry->name = xstrdup_replace_suffix(line, ".o", suffix);
+		hash_file(entry);
+
+		num_files++;
+	}
+
+	if (ferror(in))
+		errx(1, "Failed to read %s", fname);
+
+	fclose(in);
+}
+
+static __attribute__((noreturn))
+void usage(void)
+{
+	fprintf(stderr,
+		"Usage: scripts/modules-merkle-tree <kmod suffix> <root definition>\n");
+	exit(2);
+}
+
+int main(int argc, char *argv[])
+{
+	const char *kmod_suffix;
+	const EVP_MD *hash_evp;
+	struct mtree *mt;
+
+	if (argc != 3)
+		usage();
+
+	kmod_suffix = argv[2];
+
+	hash_evp = EVP_sha256();
+	ERR(!hash_evp, "EVP_sha256()");
+
+	ctx = EVP_MD_CTX_new();
+	ERR(!ctx, "EVP_MD_CTX_new()");
+
+	hash_size = EVP_MD_get_size(hash_evp);
+	ERR(hash_size <= 0, "EVP_get_digestbyname");
+
+	if (hash_size != sizeof(struct hash))
+		errx(1, "Invalid hash size");
+
+	if (EVP_DigestInit_ex(ctx, hash_evp, NULL) != 1)
+		ERR(1, "EVP_DigestInit_ex()");
+
+	read_modules_order("modules.order", kmod_suffix);
+
+	mt = build_merkle(fh_list, num_files);
+	write_merkle_root(mt, argv[1]);
+	for (size_t i = 0; i < num_files; i++) {
+		char *signame;
+		int fd;
+
+		signame = xstrdup_replace_suffix(fh_list[i].name, kmod_suffix, ".merkle");
+
+		fd = open(signame, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+		if (fd < 0)
+			err(1, "Can't create %s", signame);
+
+		build_proof(mt, i, fd);
+		append_module_signature_magic(fd, lseek(fd, 0, SEEK_CUR));
+		if (close(fd))
+			err(1, "Can't write %s", signame);
+	}
+
+	free_mtree(mt);
+	for (size_t i = 0; i < num_files; i++)
+		free(fh_list[i].name);
+	free(fh_list);
+
+	EVP_MD_CTX_free(ctx);
+	return 0;
+}
diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig
index 155959205b8e..60b240e3ef1f 100644
--- a/security/lockdown/Kconfig
+++ b/security/lockdown/Kconfig
@@ -1,7 +1,7 @@
 config SECURITY_LOCKDOWN_LSM
 	bool "Basic module for enforcing kernel lockdown"
 	depends on SECURITY
-	depends on !MODULES || MODULE_SIG
+	depends on !MODULES || MODULE_SIG || MODULE_HASHES
 	help
 	  Build support for an LSM that enforces a coarse kernel lockdown
 	  behaviour.
diff --git a/tools/include/uapi/linux/module_signature.h b/tools/include/uapi/linux/module_signature.h
index 634c9f1c8fc2..78e206996eed 100644
--- a/tools/include/uapi/linux/module_signature.h
+++ b/tools/include/uapi/linux/module_signature.h
@@ -16,6 +16,7 @@
 
 enum module_signature_type {
 	MODULE_SIGNATURE_TYPE_PKCS7 = 2,	/* Signature in PKCS#7 message */
+	MODULE_SIGNATURE_TYPE_MERKLE = 3,	/* Merkle proof for modules, opaque structure */
 };
 
 /*

-- 
2.54.0


^ permalink raw reply related

* [PATCH v5 13/14] kbuild: move handling of module stripping to Makefile.lib
From: Thomas Weißschuh @ 2026-05-05  9:05 UTC (permalink / raw)
  To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, Nathan Chancellor,
	Nicolas Schier, Arnd Bergmann, Luis Chamberlain, Petr Pavlu,
	Sami Tolvanen, Daniel Gomez, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Madhavan Srinivasan,
	Michael Ellerman, Nicholas Piggin, Naveen N Rao, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Eric Snowberg, Nicolas Schier,
	Daniel Gomez, Aaron Tomlin, Christophe Leroy (CS GROUP),
	Nicolas Bouchinet, Xiu Jianfeng, Christophe Leroy
  Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa, bpf,
	Fabian Grünbichler, Arnout Engelen, Mattia Rizzolo, kpcyrd,
	Christian Heusel, Câju Mihai-Drosi, Eric Biggers,
	Sebastian Andrzej Siewior, linux-kbuild, linux-kernel, linux-arch,
	linux-modules, linux-security-module, linux-doc, linuxppc-dev,
	linux-integrity, debian-kernel, Thomas Weißschuh
In-Reply-To: <20260505-module-hashes-v5-0-e174a5a49fce@weissschuh.net>

To allow CONFIG_MODULE_HASHES in combination with INSTALL_MOD_STRIP,
this logc will also be used by Makefile.modfinal.

Move it to a shared location to enable reuse.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 scripts/Makefile.lib     | 32 ++++++++++++++++++++++++++++++++
 scripts/Makefile.modinst | 37 +++++--------------------------------
 2 files changed, 37 insertions(+), 32 deletions(-)

diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 0718e39cedda..bb713a1a11be 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -484,6 +484,38 @@ define sed-offsets
 	s:->::; p;}'
 endef
 
+#
+# Module Installation
+#
+quiet_cmd_install_mod = INSTALL $@
+      cmd_install_mod = cp $< $@
+
+# Module Strip
+# ---------------------------------------------------------------------------
+#
+# INSTALL_MOD_STRIP, if defined, will cause modules to be stripped after they
+# are installed. If INSTALL_MOD_STRIP is '1', then the default option
+# --strip-debug will be used. Otherwise, INSTALL_MOD_STRIP value will be used
+# as the options to the strip command.
+ifeq ($(INSTALL_MOD_STRIP),1)
+mod-strip-option := --strip-debug
+else
+mod-strip-option := $(INSTALL_MOD_STRIP)
+endif
+
+# Strip
+ifdef INSTALL_MOD_STRIP
+
+quiet_cmd_strip_mod = STRIP   $@
+      cmd_strip_mod = $(STRIP) $(mod-strip-option) $@
+
+else
+
+quiet_cmd_strip_mod =
+      cmd_strip_mod = :
+
+endif
+
 # Use filechk to avoid rebuilds when a header changes, but the resulting file
 # does not
 define filechk_offsets
diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst
index 68708a039a62..b95f613e23c8 100644
--- a/scripts/Makefile.modinst
+++ b/scripts/Makefile.modinst
@@ -8,6 +8,7 @@ __modinst:
 
 include $(objtree)/include/config/auto.conf
 include $(srctree)/scripts/Kbuild.include
+include $(srctree)/scripts/Makefile.lib
 
 install-y :=
 
@@ -36,7 +37,7 @@ install-y += $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo)
 install-$(CONFIG_BUILTIN_MODULE_RANGES) += $(MODLIB)/modules.builtin.ranges
 
 $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo modules.builtin.ranges): $(MODLIB)/%: % FORCE
-	$(call cmd,install)
+	$(call cmd,install_mod)
 
 endif
 
@@ -65,40 +66,12 @@ install-$(CONFIG_MODULES) += $(modules)
 __modinst: $(install-y)
 	@:
 
-#
-# Installation
-#
-quiet_cmd_install = INSTALL $@
-      cmd_install = cp $< $@
-
-# Strip
-#
-# INSTALL_MOD_STRIP, if defined, will cause modules to be stripped after they
-# are installed. If INSTALL_MOD_STRIP is '1', then the default option
-# --strip-debug will be used. Otherwise, INSTALL_MOD_STRIP value will be used
-# as the options to the strip command.
-ifdef INSTALL_MOD_STRIP
-
 ifdef CONFIG_MODULE_HASHES
 ifeq ($(KBUILD_EXTMOD),)
+ifdef INSTALL_MOD_STRIP
 $(error CONFIG_MODULE_HASHES and INSTALL_MOD_STRIP are mutually exclusive)
 endif
 endif
-
-ifeq ($(INSTALL_MOD_STRIP),1)
-strip-option := --strip-debug
-else
-strip-option := $(INSTALL_MOD_STRIP)
-endif
-
-quiet_cmd_strip = STRIP   $@
-      cmd_strip = $(STRIP) $(strip-option) $@
-
-else
-
-quiet_cmd_strip =
-      cmd_strip = :
-
 endif
 
 #
@@ -131,8 +104,8 @@ endif
 $(foreach dir, $(sort $(dir $(install-y))), $(shell mkdir -p $(dir)))
 
 $(dst)/%.ko: %.ko FORCE
-	$(call cmd,install)
-	$(call cmd,strip)
+	$(call cmd,install_mod)
+	$(call cmd,strip_mod)
 	$(call cmd,sign)
 
 ifdef CONFIG_MODULES

-- 
2.54.0


^ permalink raw reply related

* [RFC PATCH 0/3] initalise ff-a after finalising pKVM
From: Yeoreum Yun @ 2026-05-05  9:54 UTC (permalink / raw)
  To: linux-integrity, keyrings, linux-security-module, linux-kernel,
	linux-arm-kernel, kvmarm
  Cc: jarkko, zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg,
	paul, jmorris, serge, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, sudeep.holla, Yeoreum Yun

This patch is split out from the patchset [0] --
fix FF-A call failure with pKVM when the FF-A driver is built-in,
specifically the IMA-related part.

When pKVM is enabled, the FF-A driver must be initialised after pKVM.
Otherwise, pKVM cannot negotiate the FF-A version or obtain the RX/TX
buffer information, leading to failures in FF-A calls.

Currently, pKVM initialisation completes at device_initcall_sync,
while ffa_init() runs at the device_initcall level.

So far, linker deployes kvm_arm_init() before ffa_init(), and SMCs can
still be trapped even before finalise_pkvm() is invoked.
As a result, this issue has not been observed.

However, relying on above stuff is fragile.
Therefore, when pKVM is enabled, the FF-A infrastructure should be
initialised only after pKVM initialisation has been fully finalised.

To achieve this, introduce an ffa_root_dev ("arm-ffa") and
a corresponding driver to defer initialisation of the FF-A infrastructure
until pKVM initialisation is complete, and to defer probing of all FF-A devices until then
when pKVM is enabled.

This patch is based on v7.1-rc2

Question:

FF-A initialisation can occur at late_initcall. Because it may be deferred,
some FF-A requests cannot be serviced at that stage.
A typical example is the EFI runtime variable service using DIRECT_MSG_REQ.

Depending on the platform, the EFI runtime variable service runs with StandaloneMm
and uses FF-A DIRECT_REQ. However, when pKVM is enabled, FF-A initialisation
may be deferred to late_initcall. In this case, load_uefi_certs()
can fail if it is invoked before the FF-A driver is initialised
via deferred_probe_initcall().

Moving load_uefi_certs() to late_initcall_sync, as in the third patch,
seems not to have any problem since late_initcall and
late_initcall_sync are both of do_basic_setup() and it's before loading
init process. However, it is still unclear whether
it would be better to allow DIRECT_MSG_REQ in kvm_host_ffa_handler()
even before FF-A version negotiation since handler’s purpose seems to hook
certain memory operations, and DIRECT_MSG_REQ has been available
since FF-A specification v1.0.

Any feedback or alternative suggestions would be appreciated!

Link: https://lore.kernel.org/all/20260422162449.1814615-1-yeoreum.yun@arm.com/ [0]

Yeoreum Yun (3):
  arm64: KVM: defer kvm_init() to finalise_pkvm() when pKVM is enabled
  firmware: arm_ffa: initialise ff-a after finalising pKVM
    initialisation
  security: integrity: call load_uefi_certs() at late_initcall_sync

 arch/arm64/kvm/arm.c                          |   8 +-
 arch/arm64/kvm/pkvm.c                         |  15 ++-
 drivers/firmware/arm_ffa/bus.c                | 125 +++++++++++++++++-
 drivers/firmware/arm_ffa/common.h             |  13 +-
 drivers/firmware/arm_ffa/driver.c             |  21 ++-
 drivers/firmware/arm_ffa/smccc.c              |   2 +-
 security/integrity/platform_certs/load_uefi.c |   2 +-
 7 files changed, 166 insertions(+), 20 deletions(-)


base-commit: 7fd2df204f342fc17d1a0bfcd474b24232fb0f32
-- 
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}


^ permalink raw reply

* [RFC PATCH 1/3] arm64: KVM: defer kvm_init() to finalise_pkvm() when pKVM is enabled
From: Yeoreum Yun @ 2026-05-05  9:54 UTC (permalink / raw)
  To: linux-integrity, keyrings, linux-security-module, linux-kernel,
	linux-arm-kernel, kvmarm
  Cc: jarkko, zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg,
	paul, jmorris, serge, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, sudeep.holla, Yeoreum Yun
In-Reply-To: <20260505095409.1948371-1-yeoreum.yun@arm.com>

This patch is a preparatory change to address dependency issues
between the FF-A driver and pKVM.

kvm_init() should be invoked from finalise_pkvm(),
as this is the point where pKVM initialisation is finalised and
the system transitions into the protected mode.

Deferring kvm_init() ensures that KVM is initialised only after pKVM has
fully established its protected environment.

Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
---
 arch/arm64/kvm/arm.c  |  8 +++++---
 arch/arm64/kvm/pkvm.c | 15 ++++++++++++++-
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c
index 8bb2c7422cc8..663b1d447a9b 100644
--- a/arch/arm64/kvm/arm.c
+++ b/arch/arm64/kvm/arm.c
@@ -3025,9 +3025,11 @@ static __init int kvm_arm_init(void)
 	 * FIXME: Do something reasonable if kvm_init() fails after pKVM
 	 * hypervisor protection is finalized.
 	 */
-	err = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE);
-	if (err)
-		goto out_subs;
+	if (!is_protected_kvm_enabled()) {
+		err = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE);
+		if (err)
+			goto out_subs;
+	}
 
 	/*
 	 * This should be called after initialization is done and failure isn't
diff --git a/arch/arm64/kvm/pkvm.c b/arch/arm64/kvm/pkvm.c
index 053e4f733e4b..48b06d384570 100644
--- a/arch/arm64/kvm/pkvm.c
+++ b/arch/arm64/kvm/pkvm.c
@@ -17,6 +17,7 @@
 #include "hyp_constants.h"
 
 DEFINE_STATIC_KEY_FALSE(kvm_protected_mode_initialized);
+EXPORT_SYMBOL_GPL(kvm_protected_mode_initialized);
 
 static struct memblock_region *hyp_memory = kvm_nvhe_sym(hyp_memory);
 static unsigned int *hyp_memblock_nr_ptr = &kvm_nvhe_sym(hyp_memblock_nr);
@@ -289,10 +290,22 @@ static int __init finalize_pkvm(void)
 	kmemleak_free_part(__hyp_rodata_start, __hyp_rodata_end - __hyp_rodata_start);
 	kmemleak_free_part_phys(hyp_mem_base, hyp_mem_size);
 
-	ret = pkvm_drop_host_privileges();
+	ret = kvm_init(sizeof(struct kvm_vcpu), 0, THIS_MODULE);
 	if (ret)
+		goto out_err;
+
+	ret = pkvm_drop_host_privileges();
+	if (ret) {
 		pr_err("Failed to finalize Hyp protection: %d\n", ret);
+		kvm_exit();
+		goto out_err;
+	}
+
+	return 0;
 
+out_err:
+	kvm_unregister_perf_callbacks();
+	kvm_arm_vmid_alloc_free();
 	return ret;
 }
 device_initcall_sync(finalize_pkvm);
-- 
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}


^ permalink raw reply related

* [RFC PATCH 2/3] firmware: arm_ffa: initialise ff-a after finalising pKVM initialisation
From: Yeoreum Yun @ 2026-05-05  9:54 UTC (permalink / raw)
  To: linux-integrity, keyrings, linux-security-module, linux-kernel,
	linux-arm-kernel, kvmarm
  Cc: jarkko, zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg,
	paul, jmorris, serge, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, sudeep.holla, Yeoreum Yun
In-Reply-To: <20260505095409.1948371-1-yeoreum.yun@arm.com>

When pKVM is enabled, the FF-A driver must be initialised after pKVM.
Otherwise, pKVM cannot negotiate the FF-A version or obtain the RX/TX
buffer information, leading to failures in FF-A calls.

Currently, pKVM initialisation completes at device_initcall_sync,
while ffa_init() runs at the device_initcall level.

So far, linker deployes kvm_arm_init() before ffa_init(), and SMCs can
still be trapped even before finalise_pkvm() is invoked.
As a result, this issue has not been observed.

However, relying on above stuff is fragile.
Therefore, when pKVM is enabled, the FF-A infrastructure should be
initialised only after pKVM initialisation has been fully finalised.

To achieve this, introduce an ffa_root_dev ("arm-ffa") and
a corresponding driver to defer initialisation of the FF-A infrastructure
until pKVM initialisation is complete, and to defer probing of all FF-A devices until then
when pKVM is enabled.

This change slightly alters the sysfs layout.

Previously, all FF-A devices were located directly under /sys/devices/,
for example:

  ls /sys/devices/
  ...
  arm-ffa-1
  arm-ffa-2
  arm-ffa-3
  ...

After this patch, all FF-A devices are placed under the FF-A root device
/sys/devices/arm-ffa/:

  ls /sys/devices/arm-ffa/
  ...
  arm-ffa-1
  arm-ffa-2
  arm-ffa-3
  ...

Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
---
 drivers/firmware/arm_ffa/bus.c    | 125 ++++++++++++++++++++++++++++--
 drivers/firmware/arm_ffa/common.h |  13 +++-
 drivers/firmware/arm_ffa/driver.c |  21 +++--
 drivers/firmware/arm_ffa/smccc.c  |   2 +-
 4 files changed, 146 insertions(+), 15 deletions(-)

diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c
index 9576862d89c4..ddb352806d59 100644
--- a/drivers/firmware/arm_ffa/bus.c
+++ b/drivers/firmware/arm_ffa/bus.c
@@ -13,11 +13,14 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 
+#include <asm/virt.h>
+
 #include "common.h"
 
 #define FFA_UEVENT_MODALIAS_FMT	"arm_ffa:%04x:%pUb"
 
 static DEFINE_IDA(ffa_bus_id);
+static struct ffa_device *ffa_root_dev;
 
 static int ffa_device_match(struct device *dev, const struct device_driver *drv)
 {
@@ -27,7 +30,13 @@ static int ffa_device_match(struct device *dev, const struct device_driver *drv)
 	id_table = to_ffa_driver(drv)->id_table;
 	ffa_dev = to_ffa_dev(dev);
 
-	while (!uuid_is_null(&id_table->uuid)) {
+	if (is_ffa_root_device(ffa_dev)) {
+		if (!id_table)
+			return 1;
+		return 0;
+	}
+
+	while (id_table && !uuid_is_null(&id_table->uuid)) {
 		/*
 		 * FF-A v1.0 doesn't provide discovery of UUIDs, just the
 		 * partition IDs, so match it unconditionally here and handle
@@ -50,7 +59,7 @@ static int ffa_device_probe(struct device *dev)
 	struct ffa_device *ffa_dev = to_ffa_dev(dev);
 
 	/* UUID can be still NULL with FF-A v1.0, so just skip probing them */
-	if (uuid_is_null(&ffa_dev->uuid))
+	if (!is_ffa_root_device(ffa_dev) && uuid_is_null(&ffa_dev->uuid))
 		return -ENODEV;
 
 	return ffa_drv->probe(ffa_dev);
@@ -68,6 +77,9 @@ static int ffa_device_uevent(const struct device *dev, struct kobj_uevent_env *e
 {
 	const struct ffa_device *ffa_dev = to_ffa_dev(dev);
 
+	if (is_ffa_root_device(ffa_dev))
+		return 0;
+
 	return add_uevent_var(env, "MODALIAS=" FFA_UEVENT_MODALIAS_FMT,
 			      ffa_dev->vm_id, &ffa_dev->uuid);
 }
@@ -114,7 +126,6 @@ const struct bus_type ffa_bus_type = {
 	.probe		= ffa_device_probe,
 	.remove		= ffa_device_remove,
 	.uevent		= ffa_device_uevent,
-	.dev_groups	= ffa_device_attributes_groups,
 };
 EXPORT_SYMBOL_GPL(ffa_bus_type);
 
@@ -149,13 +160,18 @@ static void ffa_release_device(struct device *dev)
 {
 	struct ffa_device *ffa_dev = to_ffa_dev(dev);
 
-	ida_free(&ffa_bus_id, ffa_dev->id);
+	if (!is_ffa_root_device(ffa_dev))
+		ida_free(&ffa_bus_id, ffa_dev->id);
 	kfree(ffa_dev);
 }
 
 static int __ffa_devices_unregister(struct device *dev, void *data)
 {
-	device_unregister(dev);
+	struct ffa_device *ffa_dev = to_ffa_dev(dev);
+
+	/* ffa_root_dev will be removed in arm_ffa_bus_exit(). */
+	if (!is_ffa_root_device(ffa_dev))
+		device_unregister(dev);
 
 	return 0;
 }
@@ -195,6 +211,10 @@ ffa_device_register(const struct ffa_partition_info *part_info,
 	int id, ret;
 	struct device *dev;
 	struct ffa_device *ffa_dev;
+	struct device_link *link;
+
+	if (!ffa_root_dev)
+		return NULL;
 
 	if (!part_info)
 		return NULL;
@@ -213,6 +233,8 @@ ffa_device_register(const struct ffa_partition_info *part_info,
 	dev->bus = &ffa_bus_type;
 	dev->release = ffa_release_device;
 	dev->dma_mask = &dev->coherent_dma_mask;
+	dev->parent = &ffa_root_dev->dev;
+	dev->groups = ffa_device_attributes_groups;
 	dev_set_name(&ffa_dev->dev, "arm-ffa-%d", id);
 
 	ffa_dev->id = id;
@@ -221,7 +243,18 @@ ffa_device_register(const struct ffa_partition_info *part_info,
 	ffa_dev->ops = ops;
 	uuid_copy(&ffa_dev->uuid, &part_info->uuid);
 
-	ret = device_register(&ffa_dev->dev);
+	device_initialize(dev);
+
+	link = device_link_add(dev, &ffa_root_dev->dev,
+			       DL_FLAG_PM_RUNTIME | DL_FLAG_AUTOPROBE_CONSUMER);
+	if (!link) {
+		dev_err(dev, "unable to link device %s\n", dev_name(dev));
+		device_link_del(link);
+		put_device(dev);
+		return NULL;
+	}
+
+	ret = device_add(dev);
 	if (ret) {
 		dev_err(dev, "unable to register device %s err=%d\n",
 			dev_name(dev), ret);
@@ -242,15 +275,93 @@ void ffa_device_unregister(struct ffa_device *ffa_dev)
 }
 EXPORT_SYMBOL_GPL(ffa_device_unregister);
 
+static int __init ffa_root_device_register(void)
+{
+	int ret;
+	struct device *dev;
+	struct ffa_device *ffa_dev;
+
+	ffa_dev = kzalloc_obj(*ffa_dev);
+	if (!ffa_dev) {
+		return -ENOMEM;
+	}
+
+	dev = &ffa_dev->dev;
+	dev->bus = &ffa_bus_type;
+	dev->release = ffa_release_device;
+	dev->dma_mask = &dev->coherent_dma_mask;
+	dev_set_name(&ffa_dev->dev, "%s", "arm-ffa");
+
+	ffa_dev->id = FFA_ROOT_DEV_ID;
+
+	ret = device_register(&ffa_dev->dev);
+	if (ret) {
+		dev_err(dev, "unable to register root device. err=%d\n", ret);
+		put_device(dev);
+		return ret;
+	}
+
+	ffa_root_dev = ffa_dev;
+
+	return 0;
+}
+
+static void ffa_root_device_unregister(void)
+{
+	ffa_device_unregister(ffa_root_dev);
+	ffa_root_dev = NULL;
+}
+
+static int ffa_root_device_probe(struct ffa_device *ffa_dev)
+{
+	if (!is_ffa_root_device(ffa_dev))
+		return -EINVAL;
+
+	if (is_protected_kvm_enabled() && !is_pkvm_initialized())
+		return -EPROBE_DEFER;
+
+	return ffa_core_init();
+}
+
+static struct ffa_driver ffa_root_dev_driver = {
+	.name = "arm-ffa",
+	.probe = ffa_root_device_probe,
+	.id_table = NULL,
+};
+
+int ffa_root_device_driver_register(void)
+{
+	return ffa_driver_register(&ffa_root_dev_driver, THIS_MODULE, KBUILD_MODNAME);
+}
+
+static void ffa_root_device_driver_unregister(void)
+{
+	ffa_driver_unregister(&ffa_root_dev_driver);
+}
+
 static int __init arm_ffa_bus_init(void)
 {
-	return bus_register(&ffa_bus_type);
+	int ret;
+
+	ret = bus_register(&ffa_bus_type);
+	if (ret)
+		return ret;
+
+	ret = ffa_root_device_register();
+	if (ret) {
+		bus_unregister(&ffa_bus_type);
+		return ret;
+	}
+
+	return 0;
 }
 subsys_initcall(arm_ffa_bus_init);
 
 static void __exit arm_ffa_bus_exit(void)
 {
 	ffa_devices_unregister();
+	ffa_root_device_unregister();
+	ffa_root_device_driver_unregister();
 	bus_unregister(&ffa_bus_type);
 	ida_destroy(&ffa_bus_id);
 }
diff --git a/drivers/firmware/arm_ffa/common.h b/drivers/firmware/arm_ffa/common.h
index 9c6425a81d0d..32ffe41c3cfc 100644
--- a/drivers/firmware/arm_ffa/common.h
+++ b/drivers/firmware/arm_ffa/common.h
@@ -10,17 +10,26 @@
 #include <linux/arm-smccc.h>
 #include <linux/err.h>
 
+#define FFA_ROOT_DEV_ID	(0)
+
 typedef struct arm_smccc_1_2_regs ffa_value_t;
 
 typedef void (ffa_fn)(ffa_value_t, ffa_value_t *);
 
 bool ffa_device_is_valid(struct ffa_device *ffa_dev);
 void ffa_device_match_uuid(struct ffa_device *ffa_dev, const uuid_t *uuid);
+int ffa_root_device_driver_register(void);
+int ffa_core_init(void);
+
+static __always_inline bool is_ffa_root_device(const struct ffa_device *ffa_dev)
+{
+	return ffa_dev->id == FFA_ROOT_DEV_ID;
+}
 
 #ifdef CONFIG_ARM_FFA_SMCCC
-int __init ffa_transport_init(ffa_fn **invoke_ffa_fn);
+int ffa_transport_init(ffa_fn **invoke_ffa_fn);
 #else
-static inline int __init ffa_transport_init(ffa_fn **invoke_ffa_fn)
+static inline int ffa_transport_init(ffa_fn **invoke_ffa_fn)
 {
 	return -EOPNOTSUPP;
 }
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index eb2782848283..c41e7ed4eac8 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -1610,6 +1610,9 @@ ffa_bus_notifier(struct notifier_block *nb, unsigned long action, void *data)
 		struct ffa_driver *ffa_drv = to_ffa_driver(dev->driver);
 		const struct ffa_device_id *id_table = ffa_drv->id_table;
 
+		if (is_ffa_root_device(fdev))
+			return NOTIFY_DONE;
+
 		/*
 		 * FF-A v1.1 provides UUID for each partition as part of the
 		 * discovery API, the discovered UUID must be populated in the
@@ -2029,7 +2032,7 @@ static void ffa_notifications_setup(void)
 	ffa_notifications_cleanup();
 }
 
-static int __init ffa_init(void)
+int ffa_core_init(void)
 {
 	int ret;
 	u32 buf_sz;
@@ -2106,16 +2109,24 @@ static int __init ffa_init(void)
 	kfree(drv_info);
 	return ret;
 }
-rootfs_initcall(ffa_init);
+
+static int __init ffa_init(void)
+{
+	return ffa_root_device_driver_register();
+}
+device_initcall(ffa_init);
 
 static void __exit ffa_exit(void)
 {
 	ffa_notifications_cleanup();
 	ffa_partitions_cleanup();
 	ffa_rxtx_unmap();
-	free_pages_exact(drv_info->tx_buffer, drv_info->rxtx_bufsz);
-	free_pages_exact(drv_info->rx_buffer, drv_info->rxtx_bufsz);
-	kfree(drv_info);
+
+	if (drv_info) {
+		free_pages_exact(drv_info->tx_buffer, drv_info->rxtx_bufsz);
+		free_pages_exact(drv_info->rx_buffer, drv_info->rxtx_bufsz);
+		kfree(drv_info);
+	}
 }
 module_exit(ffa_exit);
 
diff --git a/drivers/firmware/arm_ffa/smccc.c b/drivers/firmware/arm_ffa/smccc.c
index 4d85bfff0a4e..e6125dd9f58f 100644
--- a/drivers/firmware/arm_ffa/smccc.c
+++ b/drivers/firmware/arm_ffa/smccc.c
@@ -17,7 +17,7 @@ static void __arm_ffa_fn_hvc(ffa_value_t args, ffa_value_t *res)
 	arm_smccc_1_2_hvc(&args, res);
 }
 
-int __init ffa_transport_init(ffa_fn **invoke_ffa_fn)
+int ffa_transport_init(ffa_fn **invoke_ffa_fn)
 {
 	enum arm_smccc_conduit conduit;
 
-- 
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}


^ permalink raw reply related

* [RFC PATCH 3/3] security: integrity: call load_uefi_certs() at late_initcall_sync
From: Yeoreum Yun @ 2026-05-05  9:54 UTC (permalink / raw)
  To: linux-integrity, keyrings, linux-security-module, linux-kernel,
	linux-arm-kernel, kvmarm
  Cc: jarkko, zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg,
	paul, jmorris, serge, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, sudeep.holla, Yeoreum Yun
In-Reply-To: <20260505095409.1948371-1-yeoreum.yun@arm.com>

In the arm64 pKVM environment, all FF-A requests fail until the FF-A
driver is initialized, as the FF-A version is not negotiated with the
hypervisor beforehand.

When FF-A is built-in and pKVM is enabled, pKVM
finalises its initialization at the device_initcall_sync level,
while the FF-A driver is initialized later at the late_initcall stage
via deferred probe.

When the EFI variable service runs with StandaloneMm, it uses
FFA_DIRECT_MSG to access EFI variables. As a result,
load_uefi_certs() always fails in this environment.

To address this issue, defer load_uefi_certs() to the
late_initcall_sync level.

Signed-off-by: Yeoreum Yun <yeoreum.yun@arm.com>
---
 security/integrity/platform_certs/load_uefi.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c
index c0d6948446c3..cc2b879df5b6 100644
--- a/security/integrity/platform_certs/load_uefi.c
+++ b/security/integrity/platform_certs/load_uefi.c
@@ -235,4 +235,4 @@ static int __init load_uefi_certs(void)
 
 	return rc;
 }
-late_initcall(load_uefi_certs);
+late_initcall_sync(load_uefi_certs);
-- 
LEVI:{C3F47F37-75D8-414A-A8BA-3980EC8A46D7}


^ permalink raw reply related

* Re: [RFC PATCH 0/3] initalise ff-a after finalising pKVM
From: Ben Horgan @ 2026-05-05 10:45 UTC (permalink / raw)
  To: Yeoreum Yun, linux-integrity, keyrings, linux-security-module,
	linux-kernel, linux-arm-kernel, kvmarm
  Cc: jarkko, zohar, roberto.sassu, dmitry.kasatkin, eric.snowberg,
	paul, jmorris, serge, maz, oupton, joey.gouly, suzuki.poulose,
	yuzenghui, catalin.marinas, will, sudeep.holla
In-Reply-To: <20260505095409.1948371-1-yeoreum.yun@arm.com>

Hi Levi,

On 5/5/26 10:54, Yeoreum Yun wrote:
> This patch is split out from the patchset [0] --
> fix FF-A call failure with pKVM when the FF-A driver is built-in,
> specifically the IMA-related part.
> 
> When pKVM is enabled, the FF-A driver must be initialised after pKVM.
> Otherwise, pKVM cannot negotiate the FF-A version or obtain the RX/TX
> buffer information, leading to failures in FF-A calls.
> 
> Currently, pKVM initialisation completes at device_initcall_sync,
> while ffa_init() runs at the device_initcall level.
> 
> So far, linker deployes kvm_arm_init() before ffa_init(), and SMCs can
> still be trapped even before finalise_pkvm() is invoked.
> As a result, this issue has not been observed.
> 
> However, relying on above stuff is fragile.
> Therefore, when pKVM is enabled, the FF-A infrastructure should be
> initialised only after pKVM initialisation has been fully finalised.
> 
> To achieve this, introduce an ffa_root_dev ("arm-ffa") and
> a corresponding driver to defer initialisation of the FF-A infrastructure
> until pKVM initialisation is complete, and to defer probing of all FF-A devices until then
> when pKVM is enabled.
> 
> This patch is based on v7.1-rc2
> 
> Question:
> 
> FF-A initialisation can occur at late_initcall. Because it may be deferred,
> some FF-A requests cannot be serviced at that stage.
> A typical example is the EFI runtime variable service using DIRECT_MSG_REQ.
> 
> Depending on the platform, the EFI runtime variable service runs with StandaloneMm
> and uses FF-A DIRECT_REQ. However, when pKVM is enabled, FF-A initialisation
> may be deferred to late_initcall. In this case, load_uefi_certs()
> can fail if it is invoked before the FF-A driver is initialised
> via deferred_probe_initcall().
> 
> Moving load_uefi_certs() to late_initcall_sync, as in the third patch,
> seems not to have any problem since late_initcall and
> late_initcall_sync are both of do_basic_setup() and it's before loading
> init process. However, it is still unclear whether
> it would be better to allow DIRECT_MSG_REQ in kvm_host_ffa_handler()

The spec doesn't allow this. Looking at DEN0077A 1.2 REL0:

Section 13.2.2 says:

"If they are compatible, it enables them to determine which Framework functionalities can be used. Hence, negotiation of
the version must happen before an invocation of any other FF-A ABI."

and a bit further down

"Once the caller invokes any FF-A ABI other than FFA_VERSION, the version negotiation phase is complete."

I would have thought that an SP would only go into the waiting state once the version negotiation is done.

Thanks,

Ben

> even before FF-A version negotiation since handler’s purpose seems to hook
> certain memory operations, and DIRECT_MSG_REQ has been available
> since FF-A specification v1.0.
> 
> Any feedback or alternative suggestions would be appreciated!
> 
> Link: https://lore.kernel.org/all/20260422162449.1814615-1-yeoreum.yun@arm.com/ [0]
> 
> Yeoreum Yun (3):
>   arm64: KVM: defer kvm_init() to finalise_pkvm() when pKVM is enabled
>   firmware: arm_ffa: initialise ff-a after finalising pKVM
>     initialisation
>   security: integrity: call load_uefi_certs() at late_initcall_sync
> 
>  arch/arm64/kvm/arm.c                          |   8 +-
>  arch/arm64/kvm/pkvm.c                         |  15 ++-
>  drivers/firmware/arm_ffa/bus.c                | 125 +++++++++++++++++-
>  drivers/firmware/arm_ffa/common.h             |  13 +-
>  drivers/firmware/arm_ffa/driver.c             |  21 ++-
>  drivers/firmware/arm_ffa/smccc.c              |   2 +-
>  security/integrity/platform_certs/load_uefi.c |   2 +-
>  7 files changed, 166 insertions(+), 20 deletions(-)
> 
> 
> base-commit: 7fd2df204f342fc17d1a0bfcd474b24232fb0f32


^ permalink raw reply

* Re: [RFC PATCH 0/3] initalise ff-a after finalising pKVM
From: Yeoreum Yun @ 2026-05-05 10:51 UTC (permalink / raw)
  To: Ben Horgan
  Cc: linux-integrity, keyrings, linux-security-module, linux-kernel,
	linux-arm-kernel, kvmarm, jarkko, zohar, roberto.sassu,
	dmitry.kasatkin, eric.snowberg, paul, jmorris, serge, maz, oupton,
	joey.gouly, suzuki.poulose, yuzenghui, catalin.marinas, will,
	sudeep.holla
In-Reply-To: <8942c12e-6315-493e-98c5-d55f4e6341f4@arm.com>

Hi Ben,

> Hi Levi,
> 
> On 5/5/26 10:54, Yeoreum Yun wrote:
> > This patch is split out from the patchset [0] --
> > fix FF-A call failure with pKVM when the FF-A driver is built-in,
> > specifically the IMA-related part.
> > 
> > When pKVM is enabled, the FF-A driver must be initialised after pKVM.
> > Otherwise, pKVM cannot negotiate the FF-A version or obtain the RX/TX
> > buffer information, leading to failures in FF-A calls.
> > 
> > Currently, pKVM initialisation completes at device_initcall_sync,
> > while ffa_init() runs at the device_initcall level.
> > 
> > So far, linker deployes kvm_arm_init() before ffa_init(), and SMCs can
> > still be trapped even before finalise_pkvm() is invoked.
> > As a result, this issue has not been observed.
> > 
> > However, relying on above stuff is fragile.
> > Therefore, when pKVM is enabled, the FF-A infrastructure should be
> > initialised only after pKVM initialisation has been fully finalised.
> > 
> > To achieve this, introduce an ffa_root_dev ("arm-ffa") and
> > a corresponding driver to defer initialisation of the FF-A infrastructure
> > until pKVM initialisation is complete, and to defer probing of all FF-A devices until then
> > when pKVM is enabled.
> > 
> > This patch is based on v7.1-rc2
> > 
> > Question:
> > 
> > FF-A initialisation can occur at late_initcall. Because it may be deferred,
> > some FF-A requests cannot be serviced at that stage.
> > A typical example is the EFI runtime variable service using DIRECT_MSG_REQ.
> > 
> > Depending on the platform, the EFI runtime variable service runs with StandaloneMm
> > and uses FF-A DIRECT_REQ. However, when pKVM is enabled, FF-A initialisation
> > may be deferred to late_initcall. In this case, load_uefi_certs()
> > can fail if it is invoked before the FF-A driver is initialised
> > via deferred_probe_initcall().
> > 
> > Moving load_uefi_certs() to late_initcall_sync, as in the third patch,
> > seems not to have any problem since late_initcall and
> > late_initcall_sync are both of do_basic_setup() and it's before loading
> > init process. However, it is still unclear whether
> > it would be better to allow DIRECT_MSG_REQ in kvm_host_ffa_handler()
> 
> The spec doesn't allow this. Looking at DEN0077A 1.2 REL0:
> 
> Section 13.2.2 says:
> 
> "If they are compatible, it enables them to determine which Framework functionalities can be used. Hence, negotiation of
> the version must happen before an invocation of any other FF-A ABI."
> 
> and a bit further down
> 
> "Once the caller invokes any FF-A ABI other than FFA_VERSION, the version negotiation phase is complete."
> 
> I would have thought that an SP would only go into the waiting state once the version negotiation is done.

I mean the negotiation between hypervisor and ff-a driver.
actually the version negotiation is done with SPMC in
hyp_ffa_init() but the negotiaion between hypervisor and ff-a driver
just choose the lower version between version requested from ff-a driver
and negotiated version with hypervisor and SPMC.

So, the version negotiation is already done with SPMC
but with FF-A driver with hypervisor is not yet.
However, DIRECT_MSG_REQ has supported from v1.0 
In this situation, is there any reason not to send DIRECT_REQ_MSG?

> 
> > even before FF-A version negotiation since handler’s purpose seems to hook
> > certain memory operations, and DIRECT_MSG_REQ has been available
> > since FF-A specification v1.0.
> > 
> > Any feedback or alternative suggestions would be appreciated!
> > 
> > Link: https://lore.kernel.org/all/20260422162449.1814615-1-yeoreum.yun@arm.com/ [0]
> > 
> > Yeoreum Yun (3):
> >   arm64: KVM: defer kvm_init() to finalise_pkvm() when pKVM is enabled
> >   firmware: arm_ffa: initialise ff-a after finalising pKVM
> >     initialisation
> >   security: integrity: call load_uefi_certs() at late_initcall_sync
> > 
> >  arch/arm64/kvm/arm.c                          |   8 +-
> >  arch/arm64/kvm/pkvm.c                         |  15 ++-
> >  drivers/firmware/arm_ffa/bus.c                | 125 +++++++++++++++++-
> >  drivers/firmware/arm_ffa/common.h             |  13 +-
> >  drivers/firmware/arm_ffa/driver.c             |  21 ++-
> >  drivers/firmware/arm_ffa/smccc.c              |   2 +-
> >  security/integrity/platform_certs/load_uefi.c |   2 +-
> >  7 files changed, 166 insertions(+), 20 deletions(-)
> > 
> > 
> > base-commit: 7fd2df204f342fc17d1a0bfcd474b24232fb0f32
> 

-- 
Sincerely,
Yeoreum Yun

^ permalink raw reply

* Re: [RFC PATCH 0/3] initalise ff-a after finalising pKVM
From: Yeoreum Yun @ 2026-05-05 11:16 UTC (permalink / raw)
  To: Ben Horgan
  Cc: linux-integrity, keyrings, linux-security-module, linux-kernel,
	linux-arm-kernel, kvmarm, jarkko, zohar, roberto.sassu,
	dmitry.kasatkin, eric.snowberg, paul, jmorris, serge, maz, oupton,
	joey.gouly, suzuki.poulose, yuzenghui, catalin.marinas, will,
	sudeep.holla
In-Reply-To: <afnLpG4osopTzpip@e129823.arm.com>

> Hi Ben,
> 
> > Hi Levi,
> > 
> > On 5/5/26 10:54, Yeoreum Yun wrote:
> > > This patch is split out from the patchset [0] --
> > > fix FF-A call failure with pKVM when the FF-A driver is built-in,
> > > specifically the IMA-related part.
> > > 
> > > When pKVM is enabled, the FF-A driver must be initialised after pKVM.
> > > Otherwise, pKVM cannot negotiate the FF-A version or obtain the RX/TX
> > > buffer information, leading to failures in FF-A calls.
> > > 
> > > Currently, pKVM initialisation completes at device_initcall_sync,
> > > while ffa_init() runs at the device_initcall level.
> > > 
> > > So far, linker deployes kvm_arm_init() before ffa_init(), and SMCs can
> > > still be trapped even before finalise_pkvm() is invoked.
> > > As a result, this issue has not been observed.
> > > 
> > > However, relying on above stuff is fragile.
> > > Therefore, when pKVM is enabled, the FF-A infrastructure should be
> > > initialised only after pKVM initialisation has been fully finalised.
> > > 
> > > To achieve this, introduce an ffa_root_dev ("arm-ffa") and
> > > a corresponding driver to defer initialisation of the FF-A infrastructure
> > > until pKVM initialisation is complete, and to defer probing of all FF-A devices until then
> > > when pKVM is enabled.
> > > 
> > > This patch is based on v7.1-rc2
> > > 
> > > Question:
> > > 
> > > FF-A initialisation can occur at late_initcall. Because it may be deferred,
> > > some FF-A requests cannot be serviced at that stage.
> > > A typical example is the EFI runtime variable service using DIRECT_MSG_REQ.
> > > 
> > > Depending on the platform, the EFI runtime variable service runs with StandaloneMm
> > > and uses FF-A DIRECT_REQ. However, when pKVM is enabled, FF-A initialisation
> > > may be deferred to late_initcall. In this case, load_uefi_certs()
> > > can fail if it is invoked before the FF-A driver is initialised
> > > via deferred_probe_initcall().
> > > 
> > > Moving load_uefi_certs() to late_initcall_sync, as in the third patch,
> > > seems not to have any problem since late_initcall and
> > > late_initcall_sync are both of do_basic_setup() and it's before loading
> > > init process. However, it is still unclear whether
> > > it would be better to allow DIRECT_MSG_REQ in kvm_host_ffa_handler()
> > 
> > The spec doesn't allow this. Looking at DEN0077A 1.2 REL0:
> > 
> > Section 13.2.2 says:
> > 
> > "If they are compatible, it enables them to determine which Framework functionalities can be used. Hence, negotiation of
> > the version must happen before an invocation of any other FF-A ABI."
> > 
> > and a bit further down
> > 
> > "Once the caller invokes any FF-A ABI other than FFA_VERSION, the version negotiation phase is complete."
> > 
> > I would have thought that an SP would only go into the waiting state once the version negotiation is done.
> 
> I mean the negotiation between hypervisor and ff-a driver.
> actually the version negotiation is done with SPMC in
> hyp_ffa_init() but the negotiaion between hypervisor and ff-a driver
> just choose the lower version between version requested from ff-a driver
> and negotiated version with hypervisor and SPMC.

Sorry. re-parse the word, not choose "re-negotiate" when
FF-A driver request lowever version.

> 
> So, the version negotiation is already done with SPMC
> but with FF-A driver with hypervisor is not yet.
> However, DIRECT_MSG_REQ has supported from v1.0 
> In this situation, is there any reason not to send DIRECT_REQ_MSG?

IOW, question is that some of ff-a request can be allowed
before version negotiation with FF-A driver but
using negotiated version via hyp_ffa_init() first or not.

[...]

Thanks.

-- 
Sincerely,
Yeoreum Yun

^ permalink raw reply

* Re: [RFC PATCH 0/3] initalise ff-a after finalising pKVM
From: Ben Horgan @ 2026-05-05 11:24 UTC (permalink / raw)
  To: Yeoreum Yun
  Cc: linux-integrity, keyrings, linux-security-module, linux-kernel,
	linux-arm-kernel, kvmarm, jarkko, zohar, roberto.sassu,
	dmitry.kasatkin, eric.snowberg, paul, jmorris, serge, maz, oupton,
	joey.gouly, suzuki.poulose, yuzenghui, catalin.marinas, will,
	sudeep.holla
In-Reply-To: <afnRptWilWr7nABJ@e129823.arm.com>

Hi Levi,

On 5/5/26 12:16, Yeoreum Yun wrote:
>> Hi Ben,
>>
>>> Hi Levi,
>>>
>>> On 5/5/26 10:54, Yeoreum Yun wrote:
>>>> This patch is split out from the patchset [0] --
>>>> fix FF-A call failure with pKVM when the FF-A driver is built-in,
>>>> specifically the IMA-related part.
>>>>
>>>> When pKVM is enabled, the FF-A driver must be initialised after pKVM.
>>>> Otherwise, pKVM cannot negotiate the FF-A version or obtain the RX/TX
>>>> buffer information, leading to failures in FF-A calls.
>>>>
>>>> Currently, pKVM initialisation completes at device_initcall_sync,
>>>> while ffa_init() runs at the device_initcall level.
>>>>
>>>> So far, linker deployes kvm_arm_init() before ffa_init(), and SMCs can
>>>> still be trapped even before finalise_pkvm() is invoked.
>>>> As a result, this issue has not been observed.
>>>>
>>>> However, relying on above stuff is fragile.
>>>> Therefore, when pKVM is enabled, the FF-A infrastructure should be
>>>> initialised only after pKVM initialisation has been fully finalised.
>>>>
>>>> To achieve this, introduce an ffa_root_dev ("arm-ffa") and
>>>> a corresponding driver to defer initialisation of the FF-A infrastructure
>>>> until pKVM initialisation is complete, and to defer probing of all FF-A devices until then
>>>> when pKVM is enabled.
>>>>
>>>> This patch is based on v7.1-rc2
>>>>
>>>> Question:
>>>>
>>>> FF-A initialisation can occur at late_initcall. Because it may be deferred,
>>>> some FF-A requests cannot be serviced at that stage.
>>>> A typical example is the EFI runtime variable service using DIRECT_MSG_REQ.
>>>>
>>>> Depending on the platform, the EFI runtime variable service runs with StandaloneMm
>>>> and uses FF-A DIRECT_REQ. However, when pKVM is enabled, FF-A initialisation
>>>> may be deferred to late_initcall. In this case, load_uefi_certs()
>>>> can fail if it is invoked before the FF-A driver is initialised
>>>> via deferred_probe_initcall().
>>>>
>>>> Moving load_uefi_certs() to late_initcall_sync, as in the third patch,
>>>> seems not to have any problem since late_initcall and
>>>> late_initcall_sync are both of do_basic_setup() and it's before loading
>>>> init process. However, it is still unclear whether
>>>> it would be better to allow DIRECT_MSG_REQ in kvm_host_ffa_handler()
>>>
>>> The spec doesn't allow this. Looking at DEN0077A 1.2 REL0:
>>>
>>> Section 13.2.2 says:
>>>
>>> "If they are compatible, it enables them to determine which Framework functionalities can be used. Hence, negotiation of
>>> the version must happen before an invocation of any other FF-A ABI."
>>>
>>> and a bit further down
>>>
>>> "Once the caller invokes any FF-A ABI other than FFA_VERSION, the version negotiation phase is complete."
>>>
>>> I would have thought that an SP would only go into the waiting state once the version negotiation is done.
>>
>> I mean the negotiation between hypervisor and ff-a driver.
>> actually the version negotiation is done with SPMC in
>> hyp_ffa_init() but the negotiaion between hypervisor and ff-a driver
>> just choose the lower version between version requested from ff-a driver
>> and negotiated version with hypervisor and SPMC.
> 
> Sorry. re-parse the word, not choose "re-negotiate" when
> FF-A driver request lowever version.
> 
>>
>> So, the version negotiation is already done with SPMC
>> but with FF-A driver with hypervisor is not yet.
>> However, DIRECT_MSG_REQ has supported from v1.0 
>> In this situation, is there any reason not to send DIRECT_REQ_MSG?
> 
> IOW, question is that some of ff-a request can be allowed
> before version negotiation with FF-A driver but
> using negotiated version via hyp_ffa_init() first or not.

I don't think so. Isn't it more a continuation of the negotiation rather than a re-negotiation?

Thanks,

Ben

> 
> [...]
> 
> Thanks.
> 


^ permalink raw reply

* Re: [RFC PATCH 0/3] initalise ff-a after finalising pKVM
From: Yeoreum Yun @ 2026-05-05 11:33 UTC (permalink / raw)
  To: Ben Horgan
  Cc: linux-integrity, keyrings, linux-security-module, linux-kernel,
	linux-arm-kernel, kvmarm, jarkko, zohar, roberto.sassu,
	dmitry.kasatkin, eric.snowberg, paul, jmorris, serge, maz, oupton,
	joey.gouly, suzuki.poulose, yuzenghui, catalin.marinas, will,
	sudeep.holla
In-Reply-To: <9dd2b09b-cfb1-40e5-9fdd-1e004ad784c0@arm.com>

> Hi Levi,
> 
> On 5/5/26 12:16, Yeoreum Yun wrote:
> >> Hi Ben,
> >>
> >>> Hi Levi,
> >>>
> >>> On 5/5/26 10:54, Yeoreum Yun wrote:
> >>>> This patch is split out from the patchset [0] --
> >>>> fix FF-A call failure with pKVM when the FF-A driver is built-in,
> >>>> specifically the IMA-related part.
> >>>>
> >>>> When pKVM is enabled, the FF-A driver must be initialised after pKVM.
> >>>> Otherwise, pKVM cannot negotiate the FF-A version or obtain the RX/TX
> >>>> buffer information, leading to failures in FF-A calls.
> >>>>
> >>>> Currently, pKVM initialisation completes at device_initcall_sync,
> >>>> while ffa_init() runs at the device_initcall level.
> >>>>
> >>>> So far, linker deployes kvm_arm_init() before ffa_init(), and SMCs can
> >>>> still be trapped even before finalise_pkvm() is invoked.
> >>>> As a result, this issue has not been observed.
> >>>>
> >>>> However, relying on above stuff is fragile.
> >>>> Therefore, when pKVM is enabled, the FF-A infrastructure should be
> >>>> initialised only after pKVM initialisation has been fully finalised.
> >>>>
> >>>> To achieve this, introduce an ffa_root_dev ("arm-ffa") and
> >>>> a corresponding driver to defer initialisation of the FF-A infrastructure
> >>>> until pKVM initialisation is complete, and to defer probing of all FF-A devices until then
> >>>> when pKVM is enabled.
> >>>>
> >>>> This patch is based on v7.1-rc2
> >>>>
> >>>> Question:
> >>>>
> >>>> FF-A initialisation can occur at late_initcall. Because it may be deferred,
> >>>> some FF-A requests cannot be serviced at that stage.
> >>>> A typical example is the EFI runtime variable service using DIRECT_MSG_REQ.
> >>>>
> >>>> Depending on the platform, the EFI runtime variable service runs with StandaloneMm
> >>>> and uses FF-A DIRECT_REQ. However, when pKVM is enabled, FF-A initialisation
> >>>> may be deferred to late_initcall. In this case, load_uefi_certs()
> >>>> can fail if it is invoked before the FF-A driver is initialised
> >>>> via deferred_probe_initcall().
> >>>>
> >>>> Moving load_uefi_certs() to late_initcall_sync, as in the third patch,
> >>>> seems not to have any problem since late_initcall and
> >>>> late_initcall_sync are both of do_basic_setup() and it's before loading
> >>>> init process. However, it is still unclear whether
> >>>> it would be better to allow DIRECT_MSG_REQ in kvm_host_ffa_handler()
> >>>
> >>> The spec doesn't allow this. Looking at DEN0077A 1.2 REL0:
> >>>
> >>> Section 13.2.2 says:
> >>>
> >>> "If they are compatible, it enables them to determine which Framework functionalities can be used. Hence, negotiation of
> >>> the version must happen before an invocation of any other FF-A ABI."
> >>>
> >>> and a bit further down
> >>>
> >>> "Once the caller invokes any FF-A ABI other than FFA_VERSION, the version negotiation phase is complete."
> >>>
> >>> I would have thought that an SP would only go into the waiting state once the version negotiation is done.
> >>
> >> I mean the negotiation between hypervisor and ff-a driver.
> >> actually the version negotiation is done with SPMC in
> >> hyp_ffa_init() but the negotiaion between hypervisor and ff-a driver
> >> just choose the lower version between version requested from ff-a driver
> >> and negotiated version with hypervisor and SPMC.
> > 
> > Sorry. re-parse the word, not choose "re-negotiate" when
> > FF-A driver request lowever version.
> > 
> >>
> >> So, the version negotiation is already done with SPMC
> >> but with FF-A driver with hypervisor is not yet.
> >> However, DIRECT_MSG_REQ has supported from v1.0 
> >> In this situation, is there any reason not to send DIRECT_REQ_MSG?
> > 
> > IOW, question is that some of ff-a request can be allowed
> > before version negotiation with FF-A driver but
> > using negotiated version via hyp_ffa_init() first or not.
> 
> I don't think so. Isn't it more a continuation of the negotiation rather than a re-negotiation?

Might be. However, in the case I mentioned, I’m asking because
it’s somewhat unusual in that the FF-A request occurs without an “FF-A driver.”

If the FF-A request goes through the FF-A driver, then as you said,
it can reasonably be considered a continuation of the negotiation.
But in this case, I was wondering whether it would be acceptable to
introduce additional exception handling for situations
where an FF-A request occurs without the FF-A driver.

From that perspective, even if the FF-A request does not go through
the FF-A driver, it would ultimately still have to wait until
the FF-A driver initialization is complete.

So my question was whether certain operations could be handled
as exceptions in such cases.

Thanks.

-- 
Sincerely,
Yeoreum Yun

^ permalink raw reply

* Re: [PATCH v2 1/2] bpf: add bpf_init_inode_xattr kfunc for atomic inode labeling
From: Paul Moore @ 2026-05-05 13:49 UTC (permalink / raw)
  To: Song Liu
  Cc: David Windsor, Alexander Viro, Christian Brauner,
	Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
	Eduard Zingerman, Kumar Kartikeya Dwivedi, KP Singh,
	Matt Bobrowski, James Morris, Serge E. Hallyn, Mimi Zohar,
	Roberto Sassu, Dmitry Kasatkin, Stephen Smalley, Casey Schaufler,
	Jan Kara, John Fastabend, Martin KaFai Lau, Yonghong Song,
	Jiri Olsa, Eric Snowberg, Ondrej Mosnacek, linux-fsdevel,
	linux-kernel, bpf, linux-security-module, linux-integrity,
	selinux
In-Reply-To: <CAPhsuW5zRktottTujy_O1=8VkdJGDO71M3DVVM4ezffwT_dm+A@mail.gmail.com>

On Tue, May 5, 2026 at 5:00 AM Song Liu <song@kernel.org> wrote:
> On Tue, May 5, 2026 at 4:02 AM Paul Moore <paul@paul-moore.com> wrote:
> > On Mon, May 4, 2026 at 7:09 PM Song Liu <song@kernel.org> wrote:
> > > On Tue, May 5, 2026 at 12:42 AM Paul Moore <paul@paul-moore.com> wrote:
> > > [...]
> > > > > > Perhaps I'm simply not seeing it, but is there a check to ensure that
> > > > > > there is only one BPF LSM calling into security_inode_init_security()
> > > > > > at any given time?  With the BPF LSM only reserving a single xattr
> > > > > > slot, multiple loaded BPF LSM programs providing
> > > > > > security_inode_init_security() callbacks will be a problem.
> > > > >
> > > > > I don't think there is such a check. Also, a single BPF LSM function
> > > > > may call the kfunc multiple times, which is also problematic.
> > > > >
> > > > > I think we will need to make the default bigger, and also introduce
> > > > > some realloc mechanism for the worst case scenario. This should
> > > > > work, but the code might be a bit messy.
> > > >
> > > > Thanks for the clarification, that is what I was afraid of when
> > > > looking at the code, but I was hoping I was just missing it.
> > > >
> > > > Increasing the default is an option, but I don't think we want to
> > > > support a dynamic reallocation scheme for the xattr slots, that will
> > > > likely get extremely messy with synchronization between the LSM
> > > > framework and BPF LSM hook registrations as well as special code to
> > > > handle inodes with lifetimes that are disjoint from the BPF LSM
> > > > programs ... I suppose there may be a way to do it, but it will surely
> > > > be ugly and come at a cost.
> > >
> > > BPF trampoline already handles all the synchronizations, such as
> > > add hook, remove hook, etc. properly. So this is not that hard.
> >
> > How do you plan to handle the issue of disjoint lifetimes?
> >
> > > All we really need is to allocate a new array, copy pointers, and free
> > > the old array. And we only really need this in the worst case
> > > scenarios.
> >
> > Oh, is that all? :D
> >
> > Keep in mind that the code must also handle arbitrary ordering of
> > LSMs; in other words, you must handle a BPF LSM that isn't at the end
> > of the LSM order.  While a BPF LSM at the end of the LSM list is the
> > most common, and recommended ordering for the vast majority of users,
> > we've been working to make the ordering as generalized as possible.
>
> All the BPF LSM hooks are called together, so it should be fine.
> Maybe I missed some corner cases.

I was thinking about the LSM framework as a whole, not just the
potential for multiple BPF programs attached to the BPF LSM.

> Either way, I agree with David that we don't need too many xattrs.
> Since BPF LSM is reserved to the privileged users only, it is safe
> to put a reasonable limit, say 4 or 8, and do not handle the realloc.
> If the admin would like to brick a system with BPF LSM, there are
> many other ways to do it.

That is definitely the easier route.  However, please add code to
ensure the number of attached BPF programs is limited to the number of
requested slots.

-- 
paul-moore.com

^ permalink raw reply

* Re: [RFC PATCH 2/3] firmware: arm_ffa: initialise ff-a after finalising pKVM initialisation
From: Sudeep Holla @ 2026-05-05 14:39 UTC (permalink / raw)
  To: Yeoreum Yun
  Cc: linux-integrity, keyrings, Sudeep Holla, linux-security-module,
	linux-kernel, linux-arm-kernel, kvmarm, jarkko, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg, paul, jmorris,
	serge, maz, oupton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will
In-Reply-To: <20260505095409.1948371-3-yeoreum.yun@arm.com>

On Tue, May 05, 2026 at 10:54:08AM +0100, Yeoreum Yun wrote:
> When pKVM is enabled, the FF-A driver must be initialised after pKVM.
> Otherwise, pKVM cannot negotiate the FF-A version or obtain the RX/TX
> buffer information, leading to failures in FF-A calls.
> 
> Currently, pKVM initialisation completes at device_initcall_sync,
> while ffa_init() runs at the device_initcall level.
> 
> So far, linker deployes kvm_arm_init() before ffa_init(), and SMCs can
> still be trapped even before finalise_pkvm() is invoked.
> As a result, this issue has not been observed.
> 
> However, relying on above stuff is fragile.
> Therefore, when pKVM is enabled, the FF-A infrastructure should be
> initialised only after pKVM initialisation has been fully finalised.
> 
> To achieve this, introduce an ffa_root_dev ("arm-ffa") and
> a corresponding driver to defer initialisation of the FF-A infrastructure
> until pKVM initialisation is complete, and to defer probing of all FF-A devices until then
> when pKVM is enabled.
> 

I don't like this whole ffa root device design.
Two question for now:
1. Can FF-A be a module on systems with pKVM which removes the need for all
   this dance done here ?
2. If it is a requirement to have this built-in, I prefer to add a probe
   and defer it instead of this root ffa device.

-- 
Regards,
Sudeep

^ permalink raw reply

* Re: [RFC PATCH 2/3] firmware: arm_ffa: initialise ff-a after finalising pKVM initialisation
From: Yeoreum Yun @ 2026-05-05 15:06 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: linux-integrity, keyrings, linux-security-module, linux-kernel,
	linux-arm-kernel, kvmarm, jarkko, zohar, roberto.sassu,
	dmitry.kasatkin, eric.snowberg, paul, jmorris, serge, maz, oupton,
	joey.gouly, suzuki.poulose, yuzenghui, catalin.marinas, will
In-Reply-To: <20260505-super-gecko-of-argument-655030@sudeepholla>

Hi Sudeep,

> On Tue, May 05, 2026 at 10:54:08AM +0100, Yeoreum Yun wrote:
> > When pKVM is enabled, the FF-A driver must be initialised after pKVM.
> > Otherwise, pKVM cannot negotiate the FF-A version or obtain the RX/TX
> > buffer information, leading to failures in FF-A calls.
> > 
> > Currently, pKVM initialisation completes at device_initcall_sync,
> > while ffa_init() runs at the device_initcall level.
> > 
> > So far, linker deployes kvm_arm_init() before ffa_init(), and SMCs can
> > still be trapped even before finalise_pkvm() is invoked.
> > As a result, this issue has not been observed.
> > 
> > However, relying on above stuff is fragile.
> > Therefore, when pKVM is enabled, the FF-A infrastructure should be
> > initialised only after pKVM initialisation has been fully finalised.
> > 
> > To achieve this, introduce an ffa_root_dev ("arm-ffa") and
> > a corresponding driver to defer initialisation of the FF-A infrastructure
> > until pKVM initialisation is complete, and to defer probing of all FF-A devices until then
> > when pKVM is enabled.
> > 
> 
> I don't like this whole ffa root device design.
> Two question for now:
> 1. Can FF-A be a module on systems with pKVM which removes the need for all
>    this dance done here ?

But this means we reduce the other feature e.x) IMA with TPM over
FF-A and pKVM feature. Since IMA must be a built-in, we couldn't avoid
to build FF-A driver with built-in.

> 2. If it is a requirement to have this built-in, I prefer to add a probe
>    and defer it instead of this root ffa device.

But, How? anyway all of FF-A device & driver couldn't be probed unless
FF-A initialisation is finished and How can we trigger FF-A initailise
after pKVM finish its initialisation?

The problem is ff-a intiailisation happens before pKVM finish its
initailasation and to do defer probe, it should use dd-model and
As we discussed in other thread, notifier couldn't be a soluation.

Could you let me share other way I'm missing?

Thanks!

> -- 
> Regards,
> Sudeep

-- 
Sincerely,
Yeoreum Yun

^ permalink raw reply

* Re: [RFC PATCH 2/3] firmware: arm_ffa: initialise ff-a after finalising pKVM initialisation
From: Sudeep Holla @ 2026-05-05 16:32 UTC (permalink / raw)
  To: Yeoreum Yun
  Cc: linux-integrity, keyrings, Sudeep Holla, linux-security-module,
	linux-kernel, linux-arm-kernel, kvmarm, jarkko, zohar,
	roberto.sassu, dmitry.kasatkin, eric.snowberg, paul, jmorris,
	serge, maz, oupton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will
In-Reply-To: <afoHizU8NgFWzvYW@e129823.arm.com>

On Tue, May 05, 2026 at 04:06:51PM +0100, Yeoreum Yun wrote:
> Hi Sudeep,
> 
> > On Tue, May 05, 2026 at 10:54:08AM +0100, Yeoreum Yun wrote:
> > > When pKVM is enabled, the FF-A driver must be initialised after pKVM.
> > > Otherwise, pKVM cannot negotiate the FF-A version or obtain the RX/TX
> > > buffer information, leading to failures in FF-A calls.
> > > 
> > > Currently, pKVM initialisation completes at device_initcall_sync,
> > > while ffa_init() runs at the device_initcall level.
> > > 
> > > So far, linker deployes kvm_arm_init() before ffa_init(), and SMCs can
> > > still be trapped even before finalise_pkvm() is invoked.
> > > As a result, this issue has not been observed.
> > > 
> > > However, relying on above stuff is fragile.
> > > Therefore, when pKVM is enabled, the FF-A infrastructure should be
> > > initialised only after pKVM initialisation has been fully finalised.
> > > 
> > > To achieve this, introduce an ffa_root_dev ("arm-ffa") and
> > > a corresponding driver to defer initialisation of the FF-A infrastructure
> > > until pKVM initialisation is complete, and to defer probing of all FF-A devices until then
> > > when pKVM is enabled.
> > > 
> > 
> > I don't like this whole ffa root device design.
> > Two question for now:
> > 1. Can FF-A be a module on systems with pKVM which removes the need for all
> >    this dance done here ?
> 
> But this means we reduce the other feature e.x) IMA with TPM over
> FF-A and pKVM feature. Since IMA must be a built-in, we couldn't avoid
> to build FF-A driver with built-in.
> 
> > 2. If it is a requirement to have this built-in, I prefer to add a probe
> >    and defer it instead of this root ffa device.
> 
> But, How? anyway all of FF-A device & driver couldn't be probed unless
> FF-A initialisation is finished and How can we trigger FF-A initailise
> after pKVM finish its initialisation?
> 
> The problem is ff-a intiailisation happens before pKVM finish its
> initailasation and to do defer probe, it should use dd-model and
> As we discussed in other thread, notifier couldn't be a soluation.
> 
> Could you let me share other way I'm missing?
> 

Will something like below work ? If we add DT bindings, then we can add
of_match_table and drop the platform device creation. Also we can adjust
the parent device the way you have done by a simple change(not done in
this untested/not compiled change).

Regards,
Sudeep

-->8

firmware: arm_ffa: Register core as a platform driver

Move the FF-A core bring-up and teardown paths into platform driver
probe and remove callbacks, and register a synthetic arm-ffa platform
device to bind the driver.

This makes the FF-A core lifetime follow the driver model while keeping
the device creation internal to the FF-A core. The probe path can now
return normal driver-model errors, including probe deferral when pKVM is
enabled but not yet initialized.

Since the transport selection now happens from the platform probe path,
drop the __init annotation from ffa_transport_init().

Signed-off-by: Sudeep Holla <sudeep.holla@kernel.org>
---
 drivers/firmware/arm_ffa/common.h |  4 +--
 drivers/firmware/arm_ffa/driver.c | 55 +++++++++++++++++++++++++++----
 drivers/firmware/arm_ffa/smccc.c  |  2 +-
 3 files changed, 52 insertions(+), 9 deletions(-)

diff --git a/drivers/firmware/arm_ffa/common.h b/drivers/firmware/arm_ffa/common.h
index 9c6425a81d0d..5cdf4bd222c6 100644
--- a/drivers/firmware/arm_ffa/common.h
+++ b/drivers/firmware/arm_ffa/common.h
@@ -18,9 +18,9 @@ bool ffa_device_is_valid(struct ffa_device *ffa_dev);
 void ffa_device_match_uuid(struct ffa_device *ffa_dev, const uuid_t *uuid);
 
 #ifdef CONFIG_ARM_FFA_SMCCC
-int __init ffa_transport_init(ffa_fn **invoke_ffa_fn);
+int ffa_transport_init(ffa_fn **invoke_ffa_fn);
 #else
-static inline int __init ffa_transport_init(ffa_fn **invoke_ffa_fn)
+static inline int ffa_transport_init(ffa_fn **invoke_ffa_fn)
 {
 	return -EOPNOTSUPP;
 }
diff --git a/drivers/firmware/arm_ffa/driver.c b/drivers/firmware/arm_ffa/driver.c
index eb2782848283..d000727889f4 100644
--- a/drivers/firmware/arm_ffa/driver.c
+++ b/drivers/firmware/arm_ffa/driver.c
@@ -36,6 +36,7 @@
 #include <linux/mm.h>
 #include <linux/mutex.h>
 #include <linux/of_irq.h>
+#include <linux/platform_device.h>
 #include <linux/scatterlist.h>
 #include <linux/slab.h>
 #include <linux/smp.h>
@@ -46,6 +47,7 @@
 
 #define FFA_DRIVER_VERSION	FFA_VERSION_1_2
 #define FFA_MIN_VERSION		FFA_VERSION_1_0
+#define FFA_PLATFORM_NAME	"arm-ffa"
 
 #define SENDER_ID_MASK		GENMASK(31, 16)
 #define RECEIVER_ID_MASK	GENMASK(15, 0)
@@ -114,6 +116,7 @@ struct ffa_drv_info {
 };
 
 static struct ffa_drv_info *drv_info;
+static struct platform_device *ffa_pdev;
 
 /*
  * The driver must be able to support all the versions from the earliest
@@ -2029,12 +2032,15 @@ static void ffa_notifications_setup(void)
 	ffa_notifications_cleanup();
 }
 
-static int __init ffa_init(void)
+static int ffa_probe(struct platform_device *pdev)
 {
 	int ret;
 	u32 buf_sz;
 	size_t rxtx_bufsz = SZ_4K;
 
+	if (is_protected_kvm_enabled() && !is_pkvm_initialized())
+		return -EPROBE_DEFER;
+
 	ret = ffa_transport_init(&invoke_ffa_fn);
 	if (ret)
 		return ret;
@@ -2042,6 +2048,7 @@ static int __init ffa_init(void)
 	drv_info = kzalloc_obj(*drv_info);
 	if (!drv_info)
 		return -ENOMEM;
+	platform_set_drvdata(pdev, drv_info);
 
 	ret = ffa_version_check(&drv_info->version);
 	if (ret)
@@ -2103,19 +2110,55 @@ static int __init ffa_init(void)
 		free_pages_exact(drv_info->tx_buffer, rxtx_bufsz);
 	free_pages_exact(drv_info->rx_buffer, rxtx_bufsz);
 free_drv_info:
+	platform_set_drvdata(pdev, NULL);
 	kfree(drv_info);
+	drv_info = NULL;
 	return ret;
 }
-rootfs_initcall(ffa_init);
 
-static void __exit ffa_exit(void)
+static void ffa_remove(struct platform_device *pdev)
 {
+	struct ffa_drv_info *info = platform_get_drvdata(pdev);
+
 	ffa_notifications_cleanup();
 	ffa_partitions_cleanup();
 	ffa_rxtx_unmap();
-	free_pages_exact(drv_info->tx_buffer, drv_info->rxtx_bufsz);
-	free_pages_exact(drv_info->rx_buffer, drv_info->rxtx_bufsz);
-	kfree(drv_info);
+	free_pages_exact(info->tx_buffer, info->rxtx_bufsz);
+	free_pages_exact(info->rx_buffer, info->rxtx_bufsz);
+	kfree(info);
+	platform_set_drvdata(pdev, NULL);
+	drv_info = NULL;
+}
+
+static struct platform_driver ffa_driver = {
+	.remove = ffa_remove,
+	.driver = {
+		.name = FFA_PLATFORM_NAME,
+	},
+};
+
+static int __init ffa_init(void)
+{
+	int ret;
+
+	ffa_pdev = platform_device_register_simple(FFA_PLATFORM_NAME,
+						   PLATFORM_DEVID_NONE,
+						   NULL, 0);
+	if (IS_ERR(ffa_pdev))
+		return PTR_ERR(ffa_pdev);
+
+	ret = platform_driver_probe(&ffa_driver, ffa_probe);
+	if (ret)
+		platform_device_unregister(ffa_pdev);
+
+	return ret;
+}
+module_init(ffa_init);
+
+static void __exit ffa_exit(void)
+{
+	platform_device_unregister(ffa_pdev);
+	platform_driver_unregister(&ffa_driver);
 }
 module_exit(ffa_exit);
 
diff --git a/drivers/firmware/arm_ffa/smccc.c b/drivers/firmware/arm_ffa/smccc.c
index 4d85bfff0a4e..e6125dd9f58f 100644
--- a/drivers/firmware/arm_ffa/smccc.c
+++ b/drivers/firmware/arm_ffa/smccc.c
@@ -17,7 +17,7 @@ static void __arm_ffa_fn_hvc(ffa_value_t args, ffa_value_t *res)
 	arm_smccc_1_2_hvc(&args, res);
 }
 
-int __init ffa_transport_init(ffa_fn **invoke_ffa_fn)
+int ffa_transport_init(ffa_fn **invoke_ffa_fn)
 {
 	enum arm_smccc_conduit conduit;
 
-- 
2.43.0


^ permalink raw reply related

* Re: [RFC PATCH 2/3] firmware: arm_ffa: initialise ff-a after finalising pKVM initialisation
From: Yeoreum Yun @ 2026-05-05 16:58 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: linux-integrity, keyrings, linux-security-module, linux-kernel,
	linux-arm-kernel, kvmarm, jarkko, zohar, roberto.sassu,
	dmitry.kasatkin, eric.snowberg, paul, jmorris, serge, maz, oupton,
	joey.gouly, suzuki.poulose, yuzenghui, catalin.marinas, will
In-Reply-To: <20260505-adaptable-mussel-of-virtuosity-2751db@sudeepholla>

Hi Sudeep,

> On Tue, May 05, 2026 at 04:06:51PM +0100, Yeoreum Yun wrote:
> > Hi Sudeep,
> > 
> > > On Tue, May 05, 2026 at 10:54:08AM +0100, Yeoreum Yun wrote:
> > > > When pKVM is enabled, the FF-A driver must be initialised after pKVM.
> > > > Otherwise, pKVM cannot negotiate the FF-A version or obtain the RX/TX
> > > > buffer information, leading to failures in FF-A calls.
> > > > 
> > > > Currently, pKVM initialisation completes at device_initcall_sync,
> > > > while ffa_init() runs at the device_initcall level.
> > > > 
> > > > So far, linker deployes kvm_arm_init() before ffa_init(), and SMCs can
> > > > still be trapped even before finalise_pkvm() is invoked.
> > > > As a result, this issue has not been observed.
> > > > 
> > > > However, relying on above stuff is fragile.
> > > > Therefore, when pKVM is enabled, the FF-A infrastructure should be
> > > > initialised only after pKVM initialisation has been fully finalised.
> > > > 
> > > > To achieve this, introduce an ffa_root_dev ("arm-ffa") and
> > > > a corresponding driver to defer initialisation of the FF-A infrastructure
> > > > until pKVM initialisation is complete, and to defer probing of all FF-A devices until then
> > > > when pKVM is enabled.
> > > > 
> > > 
> > > I don't like this whole ffa root device design.
> > > Two question for now:
> > > 1. Can FF-A be a module on systems with pKVM which removes the need for all
> > >    this dance done here ?
> > 
> > But this means we reduce the other feature e.x) IMA with TPM over
> > FF-A and pKVM feature. Since IMA must be a built-in, we couldn't avoid
> > to build FF-A driver with built-in.
> > 
> > > 2. If it is a requirement to have this built-in, I prefer to add a probe
> > >    and defer it instead of this root ffa device.
> > 
> > But, How? anyway all of FF-A device & driver couldn't be probed unless
> > FF-A initialisation is finished and How can we trigger FF-A initailise
> > after pKVM finish its initialisation?
> > 
> > The problem is ff-a intiailisation happens before pKVM finish its
> > initailasation and to do defer probe, it should use dd-model and
> > As we discussed in other thread, notifier couldn't be a soluation.
> > 
> > Could you let me share other way I'm missing?
> > 
> 
> Will something like below work ?

It might work and when I write the code I thougt about whether to
use add platform device but I didn't find why this is better than
to create root device of FF-A (anyway creating a simple platform device
for FF-A seems similar to create root device)
If you don't mind why it would be better than to create FF-A root
device in FF-A bus?

> If we add DT bindings, then we can add
> of_match_table and drop the platform device creation. Also we can adjust
> the parent device the way you have done by a simple change(not done in
> this untested/not compiled change).

Might for a DT, but do we need to platform device creation for ACPI case
anyway?


[...]

-- 
Sincerely,
Yeoreum Yun

^ permalink raw reply

* Re: [PATCH v5 11/13] ima: Support staging and deleting N measurements entries
From: steven chen @ 2026-05-05 18:43 UTC (permalink / raw)
  To: Roberto Sassu, corbet, skhan, zohar, dmitry.kasatkin,
	eric.snowberg, paul, jmorris, serge
  Cc: linux-doc, linux-kernel, linux-integrity, linux-security-module,
	gregorylumen, nramas, Roberto Sassu, steven chen
In-Reply-To: <20260429160319.4162918-12-roberto.sassu@huaweicloud.com>

On 4/29/2026 9:03 AM, Roberto Sassu wrote:
> From: Roberto Sassu <roberto.sassu@huawei.com>
>
> Add support for sending a value N between 1 and ULONG_MAX to the IMA
> original measurement interface. This value represents the number of
> measurements that should be deleted from the current measurements list. In
> this case, measurements are staged in an internal non-user visible list,
> and immediately deleted.
>
> This staging method allows the remote attestation agents to easily separate
> the measurements that were verified (staged and deleted) from those that
> weren't due to the race between taking a TPM quote and reading the
> measurements list.
>
> In order to minimize the locking time of ima_extend_list_mutex, deleting
> N entries is realized by doing a lockless walk in the current measurements
> list to determine the N-th entry to cut, to cut the current measurements
> list under the lock, and by deleting the excess entries after releasing the
> lock.
>
> Flushing the hash table is not supported for N entries, since it would
> require removing the N entries one by one from the hash table under the
> ima_extend_list_mutex lock, which would increase the locking time.
>
> The ima_extend_list_mutex lock is necessary in ima_dump_measurement_list()
> because ima_queue_delete_partial() uses __list_cut_position() to modify
> ima_measurements, for which no RCU-safe variant exists. For the staging
> with prompt flavor alone, list_replace_rcu() could have been used instead,
> but since both flavors share the same kexec serialization path, the mutex
> is required regardless.

This submit provides two ways for trimming logs:
     Patch 9: stage and delete
     This patch 11: stage and delete N

Both are doing the same thing in different ways

I think the best way is just keep the patch 11 for following reasons:
     Kernel list lock time is minimum
Kernel code change will be much simpler (almost half gone)
User space processing for log trimming is much simpler
no need to maintain two lists (old and staged) in user space
No two lists seen from user space (same as before)
no staged list shown

Steven

> Link: https://github.com/linux-integrity/linux/issues/1
> Suggested-by: Steven Chen <chenste@linux.microsoft.com>
> Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
> ---
>   security/integrity/ima/Kconfig     |  3 +++
>   security/integrity/ima/ima.h       |  1 +
>   security/integrity/ima/ima_fs.c    | 21 ++++++++++++++-
>   security/integrity/ima/ima_kexec.c |  3 ++-
>   security/integrity/ima/ima_queue.c | 43 ++++++++++++++++++++++++++++++
>   5 files changed, 69 insertions(+), 2 deletions(-)
>
> diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
> index 48c906793efb..4f4373859a4f 100644
> --- a/security/integrity/ima/Kconfig
> +++ b/security/integrity/ima/Kconfig
> @@ -341,6 +341,9 @@ config IMA_STAGING
>   	  It allows user space to stage the measurements list for deletion and
>   	  to delete the staged measurements after confirmation.
>   
> +	  Or, alternatively, it allows user space to specify N measurements
> +	  entries to stage internally, so that they can be immediately deleted.
> +
>   	  On kexec, staging is reverted and staged measurements are prepended
>   	  to the current measurements list when measurements are copied to the
>   	  secondary kernel.
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 4af66c1de4dc..9a741b33d524 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -320,6 +320,7 @@ struct ima_template_desc *lookup_template_desc(const char *name);
>   bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
>   int ima_queue_stage(void);
>   int ima_queue_staged_delete_all(void);
> +int ima_queue_delete_partial(unsigned long req_value);
>   int ima_restore_measurement_entry(struct ima_template_entry *entry);
>   int ima_restore_measurement_list(loff_t bufsize, void *buf);
>   int ima_measurements_show(struct seq_file *m, void *v);
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 088d5a69aa92..6843dc203b54 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -28,6 +28,7 @@
>    * Requests:
>    * 'A\n': stage the entire measurements list
>    * 'D\n': delete all staged measurements
> + * '[1, ULONG_MAX]\n' delete N measurements entries
>    */
>   #define STAGED_REQ_LENGTH 21
>   
> @@ -312,6 +313,7 @@ static ssize_t _ima_measurements_write(struct file *file,
>   				       loff_t *ppos, bool staged_interface)
>   {
>   	char req[STAGED_REQ_LENGTH];
> +	unsigned long req_value;
>   	int ret;
>   
>   	if (*ppos > 0 || datalen < 2 || datalen > STAGED_REQ_LENGTH)
> @@ -339,7 +341,24 @@ static ssize_t _ima_measurements_write(struct file *file,
>   		ret = ima_queue_staged_delete_all();
>   		break;
>   	default:
> -		ret = -EINVAL;
> +		if (staged_interface)
> +			return -EINVAL;
> +
> +		if (ima_flush_htable) {
> +			pr_debug("Deleting staged N measurements not supported when flushing the hash table is requested\n");
> +			return -EINVAL;
> +		}
> +
> +		ret = kstrtoul(req, 10, &req_value);
> +		if (ret < 0)
> +			return ret;
> +
> +		if (req_value == 0) {
> +			pr_debug("Must delete at least one entry\n");
> +			return -EINVAL;
> +		}
> +
> +		ret = ima_queue_delete_partial(req_value);
>   	}
>   
>   	if (ret < 0)
> diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
> index 064cfce0c318..e7bde3d917b2 100644
> --- a/security/integrity/ima/ima_kexec.c
> +++ b/security/integrity/ima/ima_kexec.c
> @@ -107,7 +107,8 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
>   	memset(&khdr, 0, sizeof(khdr));
>   	khdr.version = 1;
>   	/*
> -	 * It can race with ima_queue_stage() and ima_queue_staged_delete_all().
> +	 * It can race with ima_queue_stage(), ima_queue_staged_delete_all()
> +	 * and ima_queue_delete_partial().
>   	 */
>   	mutex_lock(&ima_extend_list_mutex);
>   
> diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
> index f5c18acfbc43..64c4fe73dd5f 100644
> --- a/security/integrity/ima/ima_queue.c
> +++ b/security/integrity/ima/ima_queue.c
> @@ -371,6 +371,49 @@ int ima_queue_staged_delete_all(void)
>   	return 0;
>   }
>   
> +int ima_queue_delete_partial(unsigned long req_value)
> +{
> +	unsigned long req_value_copy = req_value;
> +	unsigned long size_to_remove = 0, num_to_remove = 0;
> +	LIST_HEAD(ima_measurements_trim);
> +	struct ima_queue_entry *qe;
> +	int ret = 0;
> +
> +	/*
> +	 * Safe to walk without rcu_read_lock(): single-writer
> +	 * exclusion in ima_fs.c prevents any concurrent modification
> +	 * to ima_measurements during this walk.
> +	 */
> +	list_for_each_entry_rcu(qe, &ima_measurements, later, true) {
> +		size_to_remove += get_binary_runtime_size(qe->entry);
> +		num_to_remove++;
> +
> +		if (--req_value_copy == 0)
> +			break;
> +	}
> +
> +	/* Not enough entries to delete. */
> +	if (req_value_copy > 0)
> +		return -ENOENT;
> +
> +	mutex_lock(&ima_extend_list_mutex);
> +	/*
> +	 * qe remains valid because ima_fs.c enforces single-writer exclusion.
> +	 */
> +	__list_cut_position(&ima_measurements_trim, &ima_measurements,
> +			    &qe->later);
> +
> +	atomic_long_sub(num_to_remove, &ima_num_entries[BINARY]);
> +
> +	if (IS_ENABLED(CONFIG_IMA_KEXEC))
> +		binary_runtime_size[BINARY] -= size_to_remove;
> +
> +	mutex_unlock(&ima_extend_list_mutex);
> +
> +	ima_queue_delete(&ima_measurements_trim, false);
> +	return ret;
> +}
> +
>   static void ima_queue_delete(struct list_head *head, bool flush_htable)
>   {
>   	struct ima_queue_entry *qe, *qe_tmp;



^ permalink raw reply

* [PATCH] tpm: svsm: constify tpm_chip_ops
From: David Windsor @ 2026-05-05 20:27 UTC (permalink / raw)
  To: Peter Huewe, Jarkko Sakkinen
  Cc: Jason Gunthorpe, linux-integrity, linux-kernel

Constify the SVSM vTPM ops. It is statically initialized and never
written to, so let's store it in .rodata.

Every other tpm_class_ops instance in drivers/char/tpm/ is already
const.

Signed-off-by: David Windsor <dwindsor@gmail.com>
---
 drivers/char/tpm/tpm_svsm.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/char/tpm/tpm_svsm.c b/drivers/char/tpm/tpm_svsm.c
index f5ba0f64850b..b74d60f687d5 100644
--- a/drivers/char/tpm/tpm_svsm.c
+++ b/drivers/char/tpm/tpm_svsm.c
@@ -49,7 +49,7 @@ static int tpm_svsm_send(struct tpm_chip *chip, u8 *buf, size_t bufsiz,
 	return svsm_vtpm_cmd_response_parse(priv->buffer, buf, bufsiz);
 }
 
-static struct tpm_class_ops tpm_chip_ops = {
+static const struct tpm_class_ops tpm_chip_ops = {
 	.flags = TPM_OPS_AUTO_STARTUP,
 	.send = tpm_svsm_send,
 };

base-commit: 254f49634ee16a731174d2ae34bc50bd5f45e731
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH] ima: debugging late_initcall_sync measurements
From: Mimi Zohar @ 2026-05-05 21:02 UTC (permalink / raw)
  To: Paul Moore
  Cc: Yeoreum Yun, Jonathan McDowell, linux-security-module,
	linux-kernel, linux-integrity, linux-arm-kernel, kvmarm, jmorris,
	serge, roberto.sassu, dmitry.kasatkin, eric.snowberg, jarkko, jgg,
	sudeep.holla, maz, oupton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will, noodles, sebastianene
In-Reply-To: <CAHC9VhQY2TMkTvQq9P8oZteQWQSr7qq2utOuH+pdVx+8jWLBCw@mail.gmail.com>

On Mon, 2026-05-04 at 16:51 -0400, Paul Moore wrote:
> On Mon, May 4, 2026 at 8:03 AM Mimi Zohar <zohar@linux.ibm.com> wrote:
> > On Sun, 2026-05-03 at 12:46 -0400, Paul Moore wrote:
> > > Regardless, assuming you always want IMA to leverage a TPMs when they
> > > exist, your reply suggests that using an initcall based IMA init
> > > scheme, even a late-sync initcall, may not be sufficient because
> > > deferred TPM initialization could happen later, yes?
> > 
> > Well yeah.  The TPM could be configured as a module, but that scenario is not of
> > interest.  That's way too late.  The case being addressed in this patch set is
> > when the TPM driver tries to initialize at device_initcall, returns
> > EPROBE_DEFER, and is retried at deferred_probe_initcall (late_initcall).  Since
> > ordering within an initcall is not supported, this patch attempts to initialize
> > IMA at late_initcall and similarly retries, in this case, at late_initcall_sync.
> 
> Okay, so from a TPM initialization perspective you are satisfied with
> a late-sync IMA initialization, yes?

No. On some architectures moving IMA initialization from the late_initcall to
late_initcall_sync does not miss any measurement records. However, as previously
mentioned, Linux running in a PowerVM LPAR the move would drop ~30 measurement
records[1].  So no, only if the TPM is not initialized by late_initcall, should
IMA retry at late_initcall_sync.

Mimi

[1]
https://lore.kernel.org/linux-integrity/201b9172ac47c6766443c1f2343cab3548f33c29.camel@linux.ibm.com/

^ permalink raw reply

* Re: [PATCH] ima: debugging late_initcall_sync measurements
From: Paul Moore @ 2026-05-05 22:55 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: Yeoreum Yun, Jonathan McDowell, linux-security-module,
	linux-kernel, linux-integrity, linux-arm-kernel, kvmarm, jmorris,
	serge, roberto.sassu, dmitry.kasatkin, eric.snowberg, jarkko, jgg,
	sudeep.holla, maz, oupton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will, noodles, sebastianene
In-Reply-To: <a9412fe10e70ce95dd70556ace19368bec5c188c.camel@linux.ibm.com>

On Tue, May 5, 2026 at 5:05 PM Mimi Zohar <zohar@linux.ibm.com> wrote:
> On Mon, 2026-05-04 at 16:51 -0400, Paul Moore wrote:
> > On Mon, May 4, 2026 at 8:03 AM Mimi Zohar <zohar@linux.ibm.com> wrote:
> > > On Sun, 2026-05-03 at 12:46 -0400, Paul Moore wrote:
> > > > Regardless, assuming you always want IMA to leverage a TPMs when they
> > > > exist, your reply suggests that using an initcall based IMA init
> > > > scheme, even a late-sync initcall, may not be sufficient because
> > > > deferred TPM initialization could happen later, yes?
> > >
> > > Well yeah.  The TPM could be configured as a module, but that scenario is not of
> > > interest.  That's way too late.  The case being addressed in this patch set is
> > > when the TPM driver tries to initialize at device_initcall, returns
> > > EPROBE_DEFER, and is retried at deferred_probe_initcall (late_initcall).  Since
> > > ordering within an initcall is not supported, this patch attempts to initialize
> > > IMA at late_initcall and similarly retries, in this case, at late_initcall_sync.
> >
> > Okay, so from a TPM initialization perspective you are satisfied with
> > a late-sync IMA initialization, yes?
>
> No. On some architectures moving IMA initialization from the late_initcall to
> late_initcall_sync does not miss any measurement records. However, as previously
> mentioned, Linux running in a PowerVM LPAR the move would drop ~30 measurement
> records[1].  So no, only if the TPM is not initialized by late_initcall, should
> IMA retry at late_initcall_sync.

What do you do in the PowerVM LPAR when the TPM is not avaiable at
late initcall and you have to defer IMA initialization until
late-sync?

-- 
paul-moore.com

^ permalink raw reply

* Re: [PATCH] ima: debugging late_initcall_sync measurements
From: Mimi Zohar @ 2026-05-06  1:51 UTC (permalink / raw)
  To: Paul Moore
  Cc: Yeoreum Yun, Jonathan McDowell, linux-security-module,
	linux-kernel, linux-integrity, linux-arm-kernel, kvmarm, jmorris,
	serge, roberto.sassu, dmitry.kasatkin, eric.snowberg, jarkko, jgg,
	sudeep.holla, maz, oupton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will, noodles, sebastianene
In-Reply-To: <CAHC9VhRUgNA=Sj=jhD=zOt8R80Q+FQj+H0nYSy-FAujTL3EKPA@mail.gmail.com>

On Tue, 2026-05-05 at 18:55 -0400, Paul Moore wrote:
> On Tue, May 5, 2026 at 5:05 PM Mimi Zohar <zohar@linux.ibm.com> wrote:
> > On Mon, 2026-05-04 at 16:51 -0400, Paul Moore wrote:
> > > On Mon, May 4, 2026 at 8:03 AM Mimi Zohar <zohar@linux.ibm.com> wrote:
> > > > On Sun, 2026-05-03 at 12:46 -0400, Paul Moore wrote:
> > > > > Regardless, assuming you always want IMA to leverage a TPMs when they
> > > > > exist, your reply suggests that using an initcall based IMA init
> > > > > scheme, even a late-sync initcall, may not be sufficient because
> > > > > deferred TPM initialization could happen later, yes?
> > > > 
> > > > Well yeah.  The TPM could be configured as a module, but that scenario is not of
> > > > interest.  That's way too late.  The case being addressed in this patch set is
> > > > when the TPM driver tries to initialize at device_initcall, returns
> > > > EPROBE_DEFER, and is retried at deferred_probe_initcall (late_initcall).  Since
> > > > ordering within an initcall is not supported, this patch attempts to initialize
> > > > IMA at late_initcall and similarly retries, in this case, at late_initcall_sync.
> > > 
> > > Okay, so from a TPM initialization perspective you are satisfied with
> > > a late-sync IMA initialization, yes?
> > 
> > No. On some architectures moving IMA initialization from the late_initcall to
> > late_initcall_sync does not miss any measurement records. However, as previously
> > mentioned, Linux running in a PowerVM LPAR the move would drop ~30 measurement
> > records[1].  So no, only if the TPM is not initialized by late_initcall, should
> > IMA retry at late_initcall_sync.
> 
> What do you do in the PowerVM LPAR when the TPM is not avaiable at
> late initcall and you have to defer IMA initialization until
> late-sync?

Your question is hypothetical, as the TPM isn't deferred, so IMA doesn't go into
TPM-bypass mode.  Testing on a PowerVM LPAR demonstrated that it skips ~30
measurement list records.  So moving the initcall to late_initcall_sync would
cause a regression.

Mimi

^ permalink raw reply

* Re: [PATCH] ima: debugging late_initcall_sync measurements
From: Paul Moore @ 2026-05-06  2:11 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: Yeoreum Yun, Jonathan McDowell, linux-security-module,
	linux-kernel, linux-integrity, linux-arm-kernel, kvmarm, jmorris,
	serge, roberto.sassu, dmitry.kasatkin, eric.snowberg, jarkko, jgg,
	sudeep.holla, maz, oupton, joey.gouly, suzuki.poulose, yuzenghui,
	catalin.marinas, will, noodles, sebastianene
In-Reply-To: <5debff82dc758d9c91223e4f1f5b9e39a3fcd4f5.camel@linux.ibm.com>

On May 5, 2026 9:57:23 PM Mimi Zohar <zohar@linux.ibm.com> wrote:
> On Tue, 2026-05-05 at 18:55 -0400, Paul Moore wrote:
>> On Tue, May 5, 2026 at 5:05 PM Mimi Zohar <zohar@linux.ibm.com> wrote:
>>> On Mon, 2026-05-04 at 16:51 -0400, Paul Moore wrote:
>>>> On Mon, May 4, 2026 at 8:03 AM Mimi Zohar <zohar@linux.ibm.com> wrote:
>>>>> On Sun, 2026-05-03 at 12:46 -0400, Paul Moore wrote:
>>>>>> Regardless, assuming you always want IMA to leverage a TPMs when they
>>>>>> exist, your reply suggests that using an initcall based IMA init
>>>>>> scheme, even a late-sync initcall, may not be sufficient because
>>>>>> deferred TPM initialization could happen later, yes?
>>>>>
>>>>> Well yeah.  The TPM could be configured as a module, but that scenario is 
>>>>> not of
>>>>> interest.  That's way too late.  The case being addressed in this patch set is
>>>>> when the TPM driver tries to initialize at device_initcall, returns
>>>>> EPROBE_DEFER, and is retried at deferred_probe_initcall (late_initcall).  Since
>>>>> ordering within an initcall is not supported, this patch attempts to initialize
>>>>> IMA at late_initcall and similarly retries, in this case, at 
>>>>> late_initcall_sync.
>>>>
>>>> Okay, so from a TPM initialization perspective you are satisfied with
>>>> a late-sync IMA initialization, yes?
>>>
>>> No. On some architectures moving IMA initialization from the late_initcall to
>>> late_initcall_sync does not miss any measurement records. However, as 
>>> previously
>>> mentioned, Linux running in a PowerVM LPAR the move would drop ~30 measurement
>>> records[1].  So no, only if the TPM is not initialized by late_initcall, should
>>> IMA retry at late_initcall_sync.
>>
>> What do you do in the PowerVM LPAR when the TPM is not avaiable at
>> late initcall and you have to defer IMA initialization until
>> late-sync?
>
> Your question is hypothetical ...

<heavy eye roll>

> ... as the TPM isn't deferred, so IMA doesn't go into
> TPM-bypass mode.  Testing on a PowerVM LPAR demonstrated that it skips ~30
> measurement list records.  So moving the initcall to late_initcall_sync would
> cause a regression.

Let me rephrase to make the question clear - how do you plan to handle a 
system where you lose measurements by waiting until late-sync, but the TPM 
is not available at the late initcall.

--
paul-moore.com




^ permalink raw reply

* Re: [PATCH] ima: debugging late_initcall_sync measurements
From: Yeoreum Yun @ 2026-05-06  5:54 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: David Safford, Jonathan McDowell, linux-security-module,
	linux-kernel, linux-integrity, linux-arm-kernel, kvmarm, paul,
	jmorris, serge, roberto.sassu, dmitry.kasatkin, eric.snowberg,
	jarkko, jgg, sudeep.holla, maz, oupton, joey.gouly,
	suzuki.poulose, yuzenghui, catalin.marinas, will, noodles,
	sebastianene
In-Reply-To: <201b9172ac47c6766443c1f2343cab3548f33c29.camel@linux.ibm.com>

Hi Mimi,

> On Sun, 2026-05-03 at 07:36 -0400, Mimi Zohar wrote:
> > On Fri, 2026-05-01 at 12:52 -0400, David Safford wrote:
> > > On Thu, Apr 30, 2026 at 5:43 PM Mimi Zohar <zohar@linux.ibm.com> wrote:
> > > > 
> > > > On Thu, 2026-04-30 at 10:48 +0100, Yeoreum Yun wrote:
> > > > > With above change I confirmed there is no meaurement log
> > > > > between boot_aggregate and boot_aggregate_late except "kernel_version"
> > > > > But this is ignorable since this UTS measurement is done in
> > > > > "ima_init_core() (old: ima_init())" and it is part of ima initialisation.
> > > > > 
> > > > > 1. ima_policy=tcb
> > > > > 
> > > > >   # cat /sys/kernel/security/ima/ascii_runtime_measurements
> > > > >   10 0adefe762c149c7cec19da62f0da1297fcfbffff ima-ng sha256:0000000000000000000000000000000000000000000000000000000000000000 boot_aggregate
> > > > >   10 4e5d73ebadfd8f850cb93ce4de755ba148a9a7d5 ima-ng sha256:0000000000000000000000000000000000000000000000000000000000000000 boot_aggregate_late
> > > > >   10 7c23cc970eceec906f7a41bc2fbde770d7092209 ima-ng sha256:72ade6ae3d35cfe5ede7a77b1c0ed1d1782a899445fdcb219c0e994a084a70d5 /bin/busybox
> > > snip
> > > > > 
> > > > > 2. ima_policy=critical_data
> > > > > 
> > > > >   # cat /sys/kernel/security/ima/ascii_runtime_measurements
> > > > >   10 0adefe762c149c7cec19da62f0da1297fcfbffff ima-ng sha256:0000000000000000000000000000000000000000000000000000000000000000 boot_aggregate
> > > > >   10 49ab61dd97ea2f759edcb6c6a3387ac67f0aa576 ima-buf sha256:0c907aab3261194f16b0c2a422a82f145bc9b9ecb8fdb633fa43e3e5379f0af2 kernel_version 372e312e302d7263312b // Ignorable since it's generated by ima_init(_core)().
> > > > >   10 4e5d73ebadfd8f850cb93ce4de755ba148a9a7d5 ima-ng sha256:0000000000000000000000000000000000000000000000000000000000000000 boot_aggregate_late
> > > > > 
> > > > > Therefore, init_ima() could move into late_initcall_sync like v1 did:
> > > > >   - https://lore.kernel.org/all/20260417175759.3191279-2-yeoreum.yun@arm.com/
> > > > 
> > > > Thanks, Yeoreum.  It's a bit premature to claim it's "safe" to move the
> > > > initcall.  Hopefully others will respond.
> > > > 
> > > > Mimi
> > > 
> > > I have also run with this patch on a number of bare metal and virtual machines,
> > > running everything from default Fedora 44 to a version with everything turned on
> > > (uefi secure boot, UKI with sdboot stub measurements, IMA measurement
> > > and appraisal enabled,
> > > all systemd measurements on, and systemd using the TPM for root
> > > partition decryption.)
> > > I too see only the kernel_version event between the normal and late
> > > calls, if ima_policy=critical_data.
> > 
> > Thanks, Dave!  Were all the systems you tested x86_64?  The next step would be
> > to test on different arch's (e.g. Z, Power).
> 
> On both Z and PowerVM, there are ~30 measurements between boot_aggregate and
> boot_aggregate_late.  For example, on PowerVM:
> 
> # grep -n boot_aggregate
> /sys/kernel/security/integrity/ima/ascii_runtime_measurements
> 
> 1:10 f60a05d7354fb34aabc02965216abd3428ea52bb ima-sig
> sha256:9887dd089ee19a6517bca10580b02c1bb9aa6cd86c157b6ead8a1c0403f348d5
> boot_aggregate 
> 31:10 e2592b0d61da6300d3db447b143897a9792231ea ima-sig
> sha256:9887dd089ee19a6517bca10580b02c1bb9aa6cd86c157b6ead8a1c0403f348d5
> boot_aggregate_late
> 
> It would be interesting to the results from a Raspberry Pi 5 as well,
> with/without a TPM.

Honestly, I find this result hard to accept.

This effectively means that there is code invoking IMA measurement during late_initcall().
It also implies that if, in the future, a late_initcall is added that performs
an IMA measurement before IMA initialization has occurred accoding to order by linker,
that measurement could be missed.

Could you please check how the index (1, 31) was added to either
ima_template_entry or ima_queue_entry to produce this result?

-- 
Sincerely,
Yeoreum Yun

^ permalink raw reply

* Re: [PATCH] tpm: svsm: constify tpm_chip_ops
From: Stefano Garzarella @ 2026-05-06  7:20 UTC (permalink / raw)
  To: David Windsor
  Cc: Peter Huewe, Jarkko Sakkinen, Jason Gunthorpe, linux-integrity,
	linux-kernel
In-Reply-To: <20260505202738.145800-1-dwindsor@gmail.com>

On Tue, May 05, 2026 at 04:27:38PM -0400, David Windsor wrote:
>Constify the SVSM vTPM ops. It is statically initialized and never
>written to, so let's store it in .rodata.
>
>Every other tpm_class_ops instance in drivers/char/tpm/ is already
>const.
>
>Signed-off-by: David Windsor <dwindsor@gmail.com>
>---
> drivers/char/tpm/tpm_svsm.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)

LGTM!

Reviewed-by: Stefano Garzarella <sgarzare@redhat.com>


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox