diff --git a/runtest/ima b/runtest/ima
index 5f4b4a7a1..29caa034a 100644
--- a/runtest/ima
+++ b/runtest/ima
@@ -5,4 +5,5 @@ ima_tpm ima_tpm.sh
 ima_violations ima_violations.sh
 ima_keys ima_keys.sh
 ima_kexec ima_kexec.sh
+ima_selinux ima_selinux.sh
 evm_overlay evm_overlay.sh
diff --git a/testcases/kernel/security/integrity/ima/README.md b/testcases/kernel/security/integrity/ima/README.md
index 68d046678..8f2249767 100644
--- a/testcases/kernel/security/integrity/ima/README.md
+++ b/testcases/kernel/security/integrity/ima/README.md
@@ -37,6 +37,25 @@ see example in `kexec.policy`.
 The test attempts to kexec the existing running kernel image.
 To kexec a different kernel image export `IMA_KEXEC_IMAGE=<pathname>`.
 
+### IMA SELinux test
+
+To enable IMA to measure SELinux state and policy, `ima_selinux.sh`
+requires a readable IMA policy, as well as a loaded measure policy with
+`measure func=CRITICAL_DATA label=selinux`,
+see example in `selinux.policy`.
+
+As well as what's required for the IMA tests, SELinux tests require reading
+the IMA policy allowed in the kernel configuration:
+```
+CONFIG_IMA_READ_POLICY=y
+```
+
+The following kernel configuration is also required. It enables compiling
+the Linux Security Module (LSM) namely SELinux.
+```
+CONFIG_SECURITY_SELINUX=y
+```
+
 ## EVM tests
 
 `evm_overlay.sh` requires a builtin IMA appraise tcb policy (e.g. `ima_policy=appraise_tcb`
diff --git a/testcases/kernel/security/integrity/ima/datafiles/Makefile b/testcases/kernel/security/integrity/ima/datafiles/Makefile
index 47a470416..280175b17 100644
--- a/testcases/kernel/security/integrity/ima/datafiles/Makefile
+++ b/testcases/kernel/security/integrity/ima/datafiles/Makefile
@@ -26,6 +26,6 @@ top_srcdir	?= ../../../../../..
 
 include	$(top_srcdir)/include/mk/env_pre.mk
 
-SUBDIRS	:= ima_kexec ima_keys ima_policy
+SUBDIRS	:= ima_kexec ima_keys ima_policy ima_selinux
 
 include $(top_srcdir)/include/mk/generic_trunk_target.mk
diff --git a/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/Makefile b/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/Makefile
new file mode 100644
index 000000000..35088fdbc
--- /dev/null
+++ b/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) Linux Test Project, 2021
+
+top_srcdir	?= ../../../../../../..
+
+include	$(top_srcdir)/include/mk/env_pre.mk
+
+INSTALL_DIR		:= testcases/data/ima_selinux
+INSTALL_TARGETS	:= *.policy
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/selinux.policy b/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/selinux.policy
new file mode 100644
index 000000000..7cbe9352d
--- /dev/null
+++ b/testcases/kernel/security/integrity/ima/datafiles/ima_selinux/selinux.policy
@@ -0,0 +1 @@
+measure func=CRITICAL_DATA label=selinux
diff --git a/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
new file mode 100755
index 000000000..e5060a5e3
--- /dev/null
+++ b/testcases/kernel/security/integrity/ima/tests/ima_selinux.sh
@@ -0,0 +1,180 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Copyright (c) 2021 Microsoft Corporation
+# Author: Lakshmi Ramasubramanian <nramas@linux.microsoft.com>
+#
+# Verify measurement of SELinux policy hash and state.
+
+TST_NEEDS_CMDS="awk cut grep tail"
+TST_CNT=2
+TST_NEEDS_DEVICE=1
+TST_SETUP="setup"
+
+. ima_setup.sh
+
+FUNC_CRITICAL_DATA='func=CRITICAL_DATA'
+REQUIRED_POLICY="^measure.*($FUNC_CRITICAL_DATA)"
+
+setup()
+{
+	SELINUX_DIR=$(tst_get_selinux_dir)
+	if [ -z "$SELINUX_DIR" ]; then
+		tst_brk TCONF "SELinux is not enabled"
+		return
+	fi
+	require_ima_policy_content "$REQUIRED_POLICY" '-E' > $TST_TMPDIR/policy.txt
+}
+
+# Format of the measured SELinux state data.
+#
+# initialized=1;enforcing=0;checkreqprot=1;
+# network_peer_controls=1;open_perms=1;extended_socket_class=1;
+# always_check_network=0;cgroup_seclabel=1;nnp_nosuid_transition=1;
+# genfs_seclabel_symlinks=0;
+validate_policy_capabilities()
+{
+	local measured_cap measured_value expected_value
+	local result=1
+	local inx=7
+
+	# Policy capabilities flags start from "network_peer_controls"
+	# in the measured SELinux state at offset 7 for 'awk'
+	while [ $inx -lt 20 ]; do
+		measured_cap=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
+		inx=$(( $inx + 1 ))
+
+		measured_value=$(echo $1 | awk -F'[=;]' -v inx="$inx" '{print $inx}')
+		expected_value=$(cat "$SELINUX_DIR/policy_capabilities/$measured_cap")
+		if [ "$measured_value" != "$expected_value" ];then
+			tst_res TWARN "$measured_cap: expected: $expected_value, got: $digest"
+			result=0
+		fi
+
+		inx=$(( $inx + 1 ))
+	done
+
+	return $result
+}
+
+# Trigger measurement of SELinux constructs and verify that
+# the measured SELinux policy hash matches the hash of the policy
+# loaded in kernel memory for SELinux.
+test1()
+{
+	local policy_digest expected_policy_digest algorithm
+	local data_source_name="selinux"
+	local pattern="data_sources=[^[:space:]]*$data_source_name"
+	local tmp_file="$TST_TMPDIR/selinux_policy_tmp_file.txt"
+
+	tst_res TINFO "verifying SELinux policy hash measurement"
+
+	# Trigger a measurement by changing SELinux state
+	tst_update_selinux_state
+
+	# Verify SELinux policy hash is measured and then validate that
+	# the measured policy hash matches the hash of the policy currently
+	# in kernel memory for SELinux
+	line=$(grep -E "selinux-policy-hash" $ASCII_MEASUREMENTS | tail -1)
+	if [ -z "$line" ]; then
+		tst_res TFAIL "SELinux policy hash not measured"
+		return
+	fi
+
+	algorithm=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f1)
+	policy_digest=$(echo "$line" | cut -d' ' -f6)
+
+	expected_policy_digest="$(compute_digest $algorithm $SELINUX_DIR/policy)" || \
+		tst_brk TCONF "cannot compute digest for $algorithm"
+
+	if [ "$policy_digest" != "$expected_policy_digest" ]; then
+		tst_res TFAIL "Digest mismatch: expected: $expected_policy_digest, got: $policy_digest"
+		return
+	fi
+
+	tst_res TPASS "SELinux policy hash measured correctly"
+}
+
+# Trigger measurement of SELinux constructs and verify that
+# the measured SELinux state matches the current SELinux
+# configuration.
+test2()
+{
+	tst_check_cmds xxd || return
+
+	local measured_data state_file="$TST_TMPDIR/selinux_state.txt"
+	local data_source_name="selinux"
+	local pattern="data_sources=[^[:space:]]*$data_source_name"
+	local tmp_file="$TST_TMPDIR/selinux_state_tmp_file.txt"
+	local digest expected_digest algorithm
+	local initialized_value
+	local enforced_value expected_enforced_value
+	local checkreqprot_value expected_checkreqprot_value
+	local result
+
+	tst_res TINFO "verifying SELinux state measurement"
+
+	# Trigger a measurement by changing SELinux state
+	tst_update_selinux_state
+
+	# Verify SELinux state is measured and then validate the measured
+	# state matches that currently set for SELinux
+	line=$(grep -E "selinux-state" $ASCII_MEASUREMENTS | tail -1)
+	if [ -z "$line" ]; then
+		tst_res TFAIL "SELinux state not measured"
+		return
+	fi
+
+	digest=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f2)
+	algorithm=$(echo "$line" | cut -d' ' -f4 | cut -d':' -f1)
+
+	echo "$line" | cut -d' ' -f6 | xxd -r -p > $state_file
+
+	expected_digest="$(compute_digest $algorithm $state_file)" || \
+	tst_brk TCONF "cannot compute digest for $algorithm"
+
+	if [ "$digest" != "$expected_digest" ]; then
+		tst_res TFAIL "digest mismatch: expected: $expected_digest, got: $digest"
+		return
+	fi
+
+	# SELinux state is measured as the following string
+	#   initialized=1;enforcing=0;checkreqprot=1;
+	# Value of 0 indicates the state is ON, and 1 indicates OFF
+	#
+	# enforce and checkreqprot measurement can be verified by
+	# comparing the value of the file "enforce" and "checkreqprot"
+	# respectively in the SELinux directory.
+	# "initialized" is an internal state and should be set to 1
+	# if enforce and checkreqprot are measured correctly.
+	measured_data=$(cat $state_file)
+	enforced_value=$(echo $measured_data | awk -F'[=;]' '{print $4}')
+	expected_enforced_value=$(cat $SELINUX_DIR/enforce)
+	if [ "$expected_enforced_value" != "$enforced_value" ];then
+		tst_res TFAIL "enforce: expected: $expected_enforced_value, got: $enforced_value"
+		return
+	fi
+
+	checkreqprot_value=$(echo $measured_data | awk -F'[=;]' '{print $6}')
+	expected_checkreqprot_value=$(cat $SELINUX_DIR/checkreqprot)
+	if [ "$expected_checkreqprot_value" != "$checkreqprot_value" ];then
+		tst_res TFAIL "checkreqprot: expected: $expected_checkreqprot_value, got: $checkreqprot_value"
+		return
+	fi
+
+	initialized_value=$(echo $measured_data | awk -F'[=;]' '{print $2}')
+	if [ "$initialized_value" != "1" ];then
+		tst_res TFAIL "initialized: expected 1, got: $initialized_value"
+		return
+	fi
+
+	validate_policy_capabilities $measured_data
+	result=$?
+	if [ $result = 0 ]; then
+		tst_res TFAIL "policy capabilities did not match"
+		return
+	fi
+
+	tst_res TPASS "SELinux state measured correctly"
+}
+
+tst_run
