diff --git a/include/linux/lsm_count.h b/include/linux/lsm_count.h
index dbb3c8573959..de8db3c77169 100644
--- a/include/linux/lsm_count.h
+++ b/include/linux/lsm_count.h
@@ -19,7 +19,7 @@
  * Capabilities is enabled when CONFIG_SECURITY is enabled.
  */
 #if IS_ENABLED(CONFIG_SECURITY)
-#define CAPABILITIES_ENABLED 1,
+#define CAPABILITIES_ENABLED 1, 1,
 #else
 #define CAPABILITIES_ENABLED
 #endif
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 135b3f58f8d2..669ee9406a62 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -215,4 +215,20 @@ extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
 extern int lsm_inode_alloc(struct inode *inode);
 extern struct lsm_static_calls_table static_calls_table __ro_after_init;
 
+/* Definition of all modular callbacks. */
+struct security_hook_mappings {
+#define LSM_HOOK(RET, DEFAULT, NAME, ...)	\
+	struct static_call_key *key_##NAME;	\
+	RET (*NAME)(__VA_ARGS__);
+#include <linux/lsm_hook_defs.h>
+} /* __randomize_layout is useless here, for this is a "const __initdata" struct. */;
+
+/* Type of individual modular callback. */
+struct security_hook_list2 {
+	struct list_head list;
+	union security_list_options hook;
+} __randomize_layout;
+
+extern int mod_lsm_add_hooks(const struct security_hook_mappings *maps);
+
 #endif /* ! __LINUX_LSM_HOOKS_H */
diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index f0386880a78e..d458b9a123d1 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -61,6 +61,7 @@ struct lsm_ctx {
 #define LSM_ID_LOCKDOWN		108
 #define LSM_ID_BPF		109
 #define LSM_ID_LANDLOCK		110
+#define LSM_ID_MOD_LSM		111
 
 /*
  * LSM_ATTR_XXX definitions identify different LSM attributes
diff --git a/security/Makefile b/security/Makefile
index 59f238490665..250b7ba23502 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -11,7 +11,7 @@ obj-$(CONFIG_SECURITY) 			+= lsm_syscalls.o
 obj-$(CONFIG_MMU)			+= min_addr.o
 
 # Object file lists
-obj-$(CONFIG_SECURITY)			+= security.o
+obj-$(CONFIG_SECURITY)			+= security.o mod_lsm.o
 obj-$(CONFIG_SECURITYFS)		+= inode.o
 obj-$(CONFIG_SECURITY_SELINUX)		+= selinux/
 obj-$(CONFIG_SECURITY_SMACK)		+= smack/
diff --git a/security/mod_lsm.c b/security/mod_lsm.c
new file mode 100644
index 000000000000..f148323b724b
--- /dev/null
+++ b/security/mod_lsm.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/lsm_hooks.h>
+
+/* List of registered modular callbacks. */
+static struct {
+#define LSM_HOOK(RET, DEFAULT, NAME, ...) struct list_head NAME;
+#include <linux/lsm_hook_defs.h>
+} mod_lsm_dynamic_hooks;
+
+/* Get LSM_CALL_ARGS_xxx definitions. */
+#include <linux/lsm_hook_args.h>
+/* A built-in callback for calling modular "int" callbacks. */
+#define LSM_INT_HOOK(RET, DEFAULT, NAME, ...)				\
+	static RET mod_lsm_##NAME(__VA_ARGS__) {			\
+		int RC = DEFAULT;					\
+		struct security_hook_list2 *P;				\
+									\
+		pr_info_once("Called %s\n", __func__);			\
+		list_for_each_entry(P, &mod_lsm_dynamic_hooks.NAME, list) { \
+			RC = P->hook.NAME(LSM_CALL_ARGS_##NAME);	\
+			if (RC != 0)					\
+				break;					\
+		}							\
+		return RC;						\
+	}
+/* A built-in callback for calling modular "void" callbacks. */
+#define LSM_VOID_HOOK(RET, DEFAULT, NAME, ...)				\
+	static RET mod_lsm_##NAME(__VA_ARGS__) {			\
+		struct security_hook_list2 *P;				\
+									\
+		pr_info_once("Called %s\n", __func__);			\
+		list_for_each_entry(P, &mod_lsm_dynamic_hooks.NAME, list) { \
+			P->hook.NAME(LSM_CALL_ARGS_##NAME);		\
+		}							\
+	}
+/* Generate all built-in callbacks here. */
+#include <linux/lsm_hook_defs.h>
+
+/* Initialize all built-in callbacks here. */
+#define LSM_HOOK(RET, DEFAULT, NAME, ...) LSM_HOOK_INIT(NAME, mod_lsm_##NAME),
+static struct security_hook_list mod_lsm_builtin_hooks[] __ro_after_init = {
+#include <linux/lsm_hook_defs.h>
+};
+
+static int mod_lsm_enabled __ro_after_init = 1;
+static struct lsm_blob_sizes mod_lsm_blob_sizes __ro_after_init = { };
+static const struct lsm_id mod_lsm_lsmid = {
+	.name = "mod_lsm",
+	.id = LSM_ID_MOD_LSM,
+};
+
+static int __init mod_lsm_init(void)
+{
+	/* Initialize modular callbacks list. */
+#define LSM_HOOK(RET, DEFAULT, NAME, ...) INIT_LIST_HEAD(&mod_lsm_dynamic_hooks.NAME);
+#include <linux/lsm_hook_defs.h>
+	/* Register built-in callbacks. */
+	security_add_hooks(mod_lsm_builtin_hooks, ARRAY_SIZE(mod_lsm_builtin_hooks), &mod_lsm_lsmid);
+	return 0;
+}
+
+DEFINE_LSM(mod_lsm) = {
+	.name = "mod_lsm",
+	.enabled = &mod_lsm_enabled,
+	.flags = 0,
+	.blobs = &mod_lsm_blob_sizes,
+	.init = mod_lsm_init,
+};
+
+/* The only exported function for registering modular callbacks. */
+int mod_lsm_add_hooks(const struct security_hook_mappings *maps)
+{
+	struct security_hook_list2 *entry;
+	int count = 0;
+
+	if (!mod_lsm_enabled) {
+		pr_info_once("Loadable LSM support is not enabled.\n");
+		return -EOPNOTSUPP;
+	}
+
+	/* Count how meny callbacks are implemented. */
+#define LSM_HOOK(RET, DEFAULT, NAME, ...) do { if (maps->NAME) count++; } while (0);
+#include <linux/lsm_hook_defs.h>
+	if (!count)
+		return -EINVAL;
+	/* Allocate memory for registering implemented callbacks. */
+	entry = kmalloc_array(count, sizeof(struct security_hook_list2), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+	/* Registering imdividual callbacks. */
+	count = 0;
+#define LSM_HOOK(RET, DEFAULT, NAME, ...) do { if (maps->NAME) {	\
+			entry[count].hook.NAME = maps->NAME;		\
+			list_add_tail(&entry[count].list, &mod_lsm_dynamic_hooks.NAME); \
+			count++;					\
+		} } while (0);
+#include <linux/lsm_hook_defs.h>
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mod_lsm_add_hooks);
diff --git a/security/security.c b/security/security.c
index 986aa5e6e29d..a34530fa042a 100644
--- a/security/security.c
+++ b/security/security.c
@@ -42,7 +42,7 @@
  * The capability module is accounted for by CONFIG_SECURITY
  */
 #define LSM_CONFIG_COUNT ( \
-	(IS_ENABLED(CONFIG_SECURITY) ? 1 : 0) + \
+	(IS_ENABLED(CONFIG_SECURITY) ? 2 : 0) + \
 	(IS_ENABLED(CONFIG_SECURITY_SELINUX) ? 1 : 0) + \
 	(IS_ENABLED(CONFIG_SECURITY_SMACK) ? 1 : 0) + \
 	(IS_ENABLED(CONFIG_SECURITY_TOMOYO) ? 1 : 0) + \
