[PATCH v2 07/19] crypto: dedicated ELF sections for collected crypto initcalls

Jay Wang
2026-04-18T00:22:31Z

Cryptographic components must be properly initialized
before use. This initialization is typically achieved
through dedicated init functions registered via wrappers
such as module_init() or late_initcall(). Traditionally,
these init functions are executed automatically as part of
the kernel boot sequence. However, now that the crypto code
is moved into a standalone module (fips140.ko), there needs
to be a way to collect and later execute them from within
the module.

To collect these init functions, the init wrappers
(module_init(), subsys_initcall(), late_initcall()) are
modified so that when compiled for the FIPS module (under
-DFIPS_MODULE), they automatically place the wrapped crypto
init function pointer into a dedicated ELF section instead
of the normal initcall mechanism. A custom linker script
crypto/fips140/fips140.lds is introduced to organize these
sections. Since the init functions must be called in proper
ordering in a later patch (e.g., subsys_initcall before
module_init, and module_init before late_initcall), the
linker script allocates separate leveled sections
(.fips_initcall0, .fips_initcall1, .fips_initcall2) with
corresponding boundary symbols (e.g.,
__fips140_initcall0_start/end) to preserve the correct
execution order.

Signed-off-by: Jay Wang <wanjay@amazon.com>
---
Makefile | 2 +-
crypto/fips140/fips140.lds | 38 ++++++++++++++++++++++++++++++++++++++
include/linux/module.h | 23 +++++++++++++++++++++++
3 files changed, 62 insertions(+), 1 deletion(-)
create mode 100644 crypto/fips140/fips140.lds

diff --git a/Makefile b/Makefile
index feacb5bd6235a..f3c43f87d6786 100644
--- a/Makefile
+++ b/Makefile
@@ -1378,7 +1378,7 @@ crypto/fips140/.fips140.symvers: fips140-ready
@:
modpost: crypto/fips140/.fips140.symvers
quiet_cmd_ld_fips140 = LD [M] $@
- cmd_ld_fips140 = $(LD) -r $(KBUILD_LDFLAGS) $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) --build-id=none --whole-archive $< --no-whole-archive -o $@
+ cmd_ld_fips140 = $(LD) -r $(KBUILD_LDFLAGS) $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) --build-id=none -T $(srctree)/crypto/fips140/fips140.lds --whole-archive $< --no-whole-archive -o $@

cmd_fips140_mod = ar -t $< > $@

diff --git a/crypto/fips140/fips140.lds b/crypto/fips140/fips140.lds
new file mode 100644
index 0000000000000..6b5c63b1c6028
--- /dev/null
+++ b/crypto/fips140/fips140.lds
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+/*
+ * FIPS 140 module initcall section layout.
+ *
+ * The overridden subsys_initcall/module_init/late_initcall macros
+ * (include/linux/module.h) place function pointers into these
+ * sections when compiled with FIPS_MODULE defined.
+ *
+ * Section mapping:
+ * .fips_initcall0 <- subsys_initcall()
+ * Syncs with kernel subsys_initcall (initcall level 4)
+ * .fips_initcall1 <- module_init()
+ * Syncs with kernel device_initcall (initcall level 6)
+ * .fips_initcall2 <- late_initcall()
+ * Syncs with kernel late_initcall (initcall level 7)
+ *
+ * The fips140 loader thread (fips140-loader.c) starts at
+ * arch_initcall_sync (level 3) and run_initcalls() in
+ * fips140-module.c executes each level in order, synchronizing
+ * with the kernel's initcall progression via wait queues.
+ */
+
+SECTIONS {
+ .init.data : {
+ __fips140_initcalls_start = .;
+ __fips140_initcall0_start = .;
+ *(.fips_initcall0)
+ __fips140_initcall0_end = .;
+ __fips140_initcall1_start = .;
+ *(.fips_initcall1)
+ __fips140_initcall1_end = .;
+ __fips140_initcall2_start = .;
+ *(.fips_initcall2)
+ __fips140_initcall2_end = .;
+ __fips140_initcalls_end = .;
+ }
+}
\ No newline at end of file
diff --git a/include/linux/module.h b/include/linux/module.h
index 0ff24c45ef61d..6a10b70b5e92c 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -115,18 +115,40 @@ extern void cleanup_module(void);
#define postcore_initcall(fn) module_init(fn)
#define postcore_initcall_sync(fn) module_init(fn)
#define arch_initcall(fn) module_init(fn)
+#if defined(CONFIG_CRYPTO_FIPS140_EXTMOD) && defined(FIPS_MODULE) && !defined(FIPS140_CORE)
+#define subsys_initcall(fn) \
+ static initcall_t __used __section(".fips_initcall0") \
+ __fips_##fn = fn;
+#else
#define subsys_initcall(fn) module_init(fn)
+#endif
#define subsys_initcall_sync(fn) module_init(fn)
#define fs_initcall(fn) module_init(fn)
#define fs_initcall_sync(fn) module_init(fn)
#define rootfs_initcall(fn) module_init(fn)
#define device_initcall(fn) module_init(fn)
#define device_initcall_sync(fn) module_init(fn)
+#if defined(CONFIG_CRYPTO_FIPS140_EXTMOD) && defined(FIPS_MODULE) && !defined(FIPS140_CORE)
+#define late_initcall(fn) \
+ static initcall_t __used __section(".fips_initcall2") \
+ __fips_##fn = fn;
+#else
#define late_initcall(fn) module_init(fn)
+#endif
#define late_initcall_sync(fn) module_init(fn)

#define console_initcall(fn) module_init(fn)

+#if defined(CONFIG_CRYPTO_FIPS140_EXTMOD) && defined(FIPS_MODULE) && !defined(FIPS140_CORE)
+/* FIPS module: place init/exit in special sections for fips140 loader */
+#define module_init(initfn) \
+ static initcall_t __used __section(".fips_initcall1") \
+ __fips_##initfn = initfn;
+
+#define module_exit(exitfn) \
+ static unsigned long __used __section(".fips_exitcall") \
+ __fips_##exitfn = (unsigned long)&exitfn;
+#else
/* Each module must use one module_init(). */
#define module_init(initfn) \
static inline initcall_t __maybe_unused __inittest(void) \
@@ -142,6 +164,7 @@ extern void cleanup_module(void);
void cleanup_module(void) __copy(exitfn) \
__attribute__((alias(#exitfn))); \
___ADDRESSABLE(cleanup_module, __exitdata);
+#endif /* CONFIG_CRYPTO_FIPS140_EXTMOD && FIPS_MODULE && !FIPS140_CORE */

#endif

--
2.47.3