diff --git a/security/selinux/Makefile b/security/selinux/Makefile
index 4d8e0e8adf0b..83d512116341 100644
--- a/security/selinux/Makefile
+++ b/security/selinux/Makefile
@@ -16,6 +16,8 @@ selinux-$(CONFIG_NETLABEL) += netlabel.o
 
 selinux-$(CONFIG_SECURITY_INFINIBAND) += ibpkey.o
 
+selinux-$(CONFIG_IMA) += measure.o
+
 ccflags-y := -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
 
 $(addprefix $(obj)/,$(selinux-y)): $(obj)/flask.h
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index efa6108b1ce9..1f270206f0ee 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -7284,6 +7284,7 @@ DEFINE_LSM(selinux) = {
 	.enabled = &selinux_enabled_boot,
 	.blobs = &selinux_blob_sizes,
 	.init = selinux_init,
+	.security_state = selinux_security_state,
 };
 
 #if defined(CONFIG_NETFILTER)
@@ -7394,6 +7395,7 @@ int selinux_disable(struct selinux_state *state)
 	}
 
 	selinux_mark_disabled(state);
+	selinux_security_state();
 
 	pr_info("SELinux:  Disabled at runtime.\n");
 
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index b0e02cfe3ce1..691c7e35f82a 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -222,16 +222,35 @@ static inline bool selinux_policycap_genfs_seclabel_symlinks(void)
 	return state->policycap[POLICYDB_CAPABILITY_GENFS_SECLABEL_SYMLINKS];
 }
 
+static inline bool selinux_checkreqprot(void)
+{
+	struct selinux_state *state = &selinux_state;
+
+	return state->checkreqprot;
+}
+
 int security_mls_enabled(struct selinux_state *state);
 int security_load_policy(struct selinux_state *state,
 			 void *data, size_t len);
 int security_read_policy(struct selinux_state *state,
 			 void **data, size_t *len);
+int security_read_selinux_policy(struct selinux_state *state,
+				 void **data, size_t *len,
+				 bool alloc_kernel_memory);
 size_t security_policydb_len(struct selinux_state *state);
 
 int security_policycap_supported(struct selinux_state *state,
 				 unsigned int req_cap);
 
+#ifdef CONFIG_IMA
+extern int selinux_security_state(void);
+#else
+static inline int selinux_security_state(void)
+{
+	return 0;
+}
+#endif
+
 #define SEL_VEC_MAX 32
 struct av_decision {
 	u32 allowed;
diff --git a/security/selinux/measure.c b/security/selinux/measure.c
new file mode 100644
index 000000000000..b909e8e61542
--- /dev/null
+++ b/security/selinux/measure.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Measure SELinux state using IMA subsystem.
+ */
+#include <linux/ima.h>
+#include "security.h"
+
+static int selinux_hash_policy(const char *hash_alg_name,
+			       void *policy, size_t policy_len,
+			       void **policy_hash, int *policy_hash_len)
+{
+	struct crypto_shash *tfm;
+	struct shash_desc *desc = NULL;
+	void *digest = NULL;
+	int desc_size;
+	int digest_size;
+	int ret = 0;
+
+	tfm = crypto_alloc_shash(hash_alg_name, 0, 0);
+	if (IS_ERR(tfm))
+		return PTR_ERR(tfm);
+
+	desc_size = crypto_shash_descsize(tfm) + sizeof(*desc);
+	digest_size = crypto_shash_digestsize(tfm);
+
+	digest = kmalloc(digest_size, GFP_KERNEL);
+	if (!digest) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	desc = kzalloc(desc_size, GFP_KERNEL);
+	if (!desc) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	desc->tfm = tfm;
+
+	ret = crypto_shash_digest(desc, policy, policy_len, digest);
+	if (ret < 0)
+		goto error;
+
+	*policy_hash_len = digest_size;
+	*policy_hash = digest;
+	digest = NULL;
+
+error:
+	kfree(desc);
+	kfree(digest);
+
+	crypto_free_shash(tfm);
+
+	if (ret)
+		pr_err("%s: error %d\n", __func__, ret);
+
+	return ret;
+}
+
+int selinux_security_state(void)
+{
+	int rc = 0;
+	int count;
+	size_t buflen;
+	int policy_hash_len;
+	char *state = NULL;
+	void *policy = NULL;
+	void *policy_hash = NULL;
+	static char *security_state_string =
+			"enabled=%d;enforcing=%d;checkreqprot=%d;"        \
+			"netpeer=%d;openperm=%d;extsockclass=%d;"         \
+			"alwaysnetwork=%d;cgroupseclabel=%d;"             \
+			"nnpnosuidtransition=%d;genfsseclabelsymlink=%d;";
+
+	if (!selinux_initialized(&selinux_state))
+		return -EOPNOTSUPP;
+
+	buflen = strlen(security_state_string) + 1;
+	state = kzalloc(buflen, GFP_KERNEL);
+	if (!state) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	count = snprintf(state, buflen, security_state_string,
+			 !selinux_disabled(&selinux_state),
+			 enforcing_enabled(&selinux_state),
+			 selinux_checkreqprot(),
+			 selinux_policycap_netpeer(),
+			 selinux_policycap_openperm(),
+			 selinux_policycap_extsockclass(),
+			 selinux_policycap_alwaysnetwork(),
+			 selinux_policycap_cgroupseclabel(),
+			 selinux_policycap_nnp_nosuid_transition(),
+			 selinux_policycap_genfs_seclabel_symlinks());
+	if (count >= 0 && count < buflen)
+		ima_lsm_state("selinux-state", state, count);
+	else {
+		pr_err("selinux state error: %d\n", count);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = security_read_selinux_policy(&selinux_state,
+					  &policy, &buflen, true);
+	if (!rc)
+		rc = selinux_hash_policy("sha256", policy, buflen,
+					 &policy_hash, &policy_hash_len);
+	if (!rc)
+		ima_lsm_state("selinux-policy-hash", policy_hash,
+			      policy_hash_len);
+
+out:
+	kfree(state);
+	kfree(policy);
+	kfree(policy_hash);
+
+	if (rc)
+		pr_err("%s: error %d\n", __func__, rc);
+
+	return rc;
+}
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 4781314c2510..8f68964e4ba8 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -173,6 +173,7 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
 			from_kuid(&init_user_ns, audit_get_loginuid(current)),
 			audit_get_sessionid(current));
 		enforcing_set(state, new_value);
+		selinux_security_state();
 		if (new_value)
 			avc_ss_reset(state->avc, 0);
 		selnl_notify_setenforce(new_value);
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
index ef0afd878bfc..0c289d13ef6a 100644
--- a/security/selinux/ss/services.c
+++ b/security/selinux/ss/services.c
@@ -3724,10 +3724,11 @@ int security_netlbl_sid_to_secattr(struct selinux_state *state,
  * security_read_policy - read the policy.
  * @data: binary policy data
  * @len: length of data in bytes
- *
+ * @alloc_kernel_memory: flag to indicate memory allocation
  */
-int security_read_policy(struct selinux_state *state,
-			 void **data, size_t *len)
+int security_read_selinux_policy(struct selinux_state *state,
+				 void **data, size_t *len,
+				 bool alloc_kernel_memory)
 {
 	struct policydb *policydb = &state->ss->policydb;
 	int rc;
@@ -3738,7 +3739,10 @@ int security_read_policy(struct selinux_state *state,
 
 	*len = security_policydb_len(state);
 
-	*data = vmalloc_user(*len);
+	if (alloc_kernel_memory)
+		*data = kzalloc(*len, GFP_KERNEL);
+	else
+		*data = vmalloc_user(*len);
 	if (!*data)
 		return -ENOMEM;
 
@@ -3754,5 +3758,16 @@ int security_read_policy(struct selinux_state *state,
 
 	*len = (unsigned long)fp.data - (unsigned long)*data;
 	return 0;
+}
 
+/**
+ * security_read_policy - read the policy.
+ * @data: binary policy data
+ * @len: length of data in bytes
+ *
+ */
+int security_read_policy(struct selinux_state *state,
+			 void **data, size_t *len)
+{
+	return security_read_selinux_policy(state, data, len, false);
 }
