tftf(rme): check if RMM doesn't leak Realm contents in SVE registers

This test verifies that the Realm contents in SVE registers are not
seen by NS world once the Realm returns back to the host. This test
performs the below steps:

1. Set NS world SVE VQ to max and write a known pattern.
2. Set NS world ZCR_EL2 with VQ as 0 (128 bits).
3. Create Realm with max SVE VQ
4. Call Realm to fill in Z registers
5. Once Realm returns, NS sets ZCR_EL2 with max VQ and reads the
   Z registers.
6. The upper bits of Z registers must be either 0 or the old values
   filled by NS world at step 1.

Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Change-Id: I8205190d1ce9c37b99d35cf5b15df21ca9b838c3
diff --git a/include/lib/extensions/sve.h b/include/lib/extensions/sve.h
index 5907ef9..60432a5 100644
--- a/include/lib/extensions/sve.h
+++ b/include/lib/extensions/sve.h
@@ -20,6 +20,7 @@
 #define SVE_VECTOR_LEN_BYTES		256
 #define SVE_NUM_VECTORS			32
 
+#define SVE_VQ_ARCH_MIN			(0U)
 #define SVE_VQ_ARCH_MAX			((1 << ZCR_EL2_SVE_VL_WIDTH) - 1)
 
 /* convert SVE VL in bytes to VQ */
@@ -28,6 +29,9 @@
 /* convert SVE VQ to bits */
 #define SVE_VQ_TO_BITS(vq)		(((vq) + 1U) << 7U)
 
+/* convert SVE VQ to bytes */
+#define SVE_VQ_TO_BYTES(vq)		(SVE_VQ_TO_BITS(vq) / 8)
+
 /* get a random SVE VQ b/w 0 to SVE_VQ_ARCH_MAX */
 #define SVE_GET_RANDOM_VQ		(rand() % (SVE_VQ_ARCH_MAX + 1))
 
diff --git a/include/runtime_services/host_realm_managment/host_shared_data.h b/include/runtime_services/host_realm_managment/host_shared_data.h
index c852d89..98ab287 100644
--- a/include/runtime_services/host_realm_managment/host_shared_data.h
+++ b/include/runtime_services/host_realm_managment/host_shared_data.h
@@ -54,7 +54,8 @@
 	REALM_SVE_RDVL,
 	REALM_SVE_ID_REGISTERS,
 	REALM_SVE_PROBE_VL,
-	REALM_SVE_OPS
+	REALM_SVE_OPS,
+	REALM_SVE_FILL_REGS
 };
 
 /*
diff --git a/realm/include/realm_tests.h b/realm/include/realm_tests.h
index 13a063a..61d4493 100644
--- a/realm/include/realm_tests.h
+++ b/realm/include/realm_tests.h
@@ -16,6 +16,7 @@
 bool test_realm_sve_read_id_registers(void);
 bool test_realm_sve_probe_vl(void);
 bool test_realm_sve_ops(void);
+bool test_realm_sve_fill_regs(void);
 
 #endif /* REALM_TESTS_H */
 
diff --git a/realm/realm_payload_main.c b/realm/realm_payload_main.c
index b13e0a1..f0ab087 100644
--- a/realm/realm_payload_main.c
+++ b/realm/realm_payload_main.c
@@ -105,6 +105,9 @@
 		case REALM_SVE_OPS:
 			test_succeed = test_realm_sve_ops();
 			break;
+		case REALM_SVE_FILL_REGS:
+			test_succeed = test_realm_sve_fill_regs();
+			break;
 		default:
 			realm_printf("%s() invalid cmd %u\n", __func__, cmd);
 			break;
diff --git a/realm/realm_sve.c b/realm/realm_sve.c
index ed6c7c7..d5ef8c1 100644
--- a/realm/realm_sve.c
+++ b/realm/realm_sve.c
@@ -20,6 +20,8 @@
 static int rl_sve_op_1[RL_SVE_OP_ARRAYSIZE];
 static int rl_sve_op_2[RL_SVE_OP_ARRAYSIZE];
 
+static sve_vector_t rl_sve_vectors_write[SVE_NUM_VECTORS] __aligned(16);
+
 /* Returns the maximum supported VL. This test is called only by sve Realm */
 bool test_realm_sve_rdvl(void)
 {
@@ -109,3 +111,20 @@
 
 	return true;
 }
+
+/* Fill SVE Z registers with known pattern */
+bool test_realm_sve_fill_regs(void)
+{
+	uint32_t vl;
+
+	assert(is_armv8_2_sve_present());
+
+	/* Config Realm with max SVE length */
+	sve_config_vq(SVE_VQ_ARCH_MAX);
+	vl = sve_vector_length_get();
+
+	memset((void *)&rl_sve_vectors_write, 0xcd, vl * SVE_NUM_VECTORS);
+	sve_fill_vector_regs(rl_sve_vectors_write);
+
+	return true;
+}
diff --git a/tftf/tests/runtime_services/realm_payload/host_realm_payload_sve_tests.c b/tftf/tests/runtime_services/realm_payload/host_realm_payload_sve_tests.c
index 4d5e075..7c8e383 100644
--- a/tftf/tests/runtime_services/realm_payload/host_realm_payload_sve_tests.c
+++ b/tftf/tests/runtime_services/realm_payload/host_realm_payload_sve_tests.c
@@ -22,6 +22,9 @@
 static int ns_sve_op_1[NS_SVE_OP_ARRAYSIZE];
 static int ns_sve_op_2[NS_SVE_OP_ARRAYSIZE];
 
+static sve_vector_t ns_sve_vectors_write[SVE_NUM_VECTORS] __aligned(16);
+static sve_vector_t ns_sve_vectors_read[SVE_NUM_VECTORS] __aligned(16);
+
 /* Skip test if SVE is not supported in H/W or in RMI features */
 #define CHECK_SVE_SUPPORT_IN_HW_AND_IN_RMI(_reg0)				\
 	do {									\
@@ -464,3 +467,89 @@
 
 	return rc;
 }
+
+/*
+ * Check if RMM leaks Realm SVE registers.
+ * This test is skipped if the supported max VQ is 128 bits, as we won't be able
+ * to run NS and Realm context with lower and higher VQ respectively.
+ * This test does the below steps:
+ *
+ * 1. Set NS SVE VQ to max and write known pattern
+ * 2. NS programs ZCR_EL2 with VQ as 0 (128 bits).
+ * 3. Create Realm with max VQ (higher than NS SVE VQ).
+ * 4. Call Realm to fill in Z registers
+ * 5. Once Realm returns, NS sets ZCR_EL2 with max VQ and reads the Z registers
+ * 6. The upper bits of Z registers must be either 0 or the old values filled by
+ *    NS world at step 1.
+ */
+test_result_t host_sve_realm_check_vectors_leaked(void)
+{
+	u_register_t rmi_feat_reg0;
+	uint8_t *regs_base_wr, *regs_base_rd;
+	test_result_t rc;
+	bool realm_rc;
+	uint8_t sve_vq;
+
+	SKIP_TEST_IF_RME_NOT_SUPPORTED_OR_RMM_IS_TRP();
+
+	CHECK_SVE_SUPPORT_IN_HW_AND_IN_RMI(rmi_feat_reg0);
+
+	sve_vq = EXTRACT(RMI_FEATURE_REGISTER_0_SVE_VL, rmi_feat_reg0);
+
+	/* Skip test if the supported max VQ is 128 bits */
+	if (sve_vq == SVE_VQ_ARCH_MIN) {
+		return TEST_RESULT_SKIPPED;
+	}
+
+	/* 1. Set NS SVE VQ to max and write known pattern */
+	sve_config_vq(sve_vq);
+	(void)memset((void *)&ns_sve_vectors_write, 0xaa,
+		     SVE_VQ_TO_BYTES(sve_vq) * SVE_NUM_VECTORS);
+	sve_fill_vector_regs(ns_sve_vectors_write);
+
+	/* 2. NS programs ZCR_EL2 with VQ as 0  */
+	sve_config_vq(SVE_VQ_ARCH_MIN);
+
+	/* 3. Create Realm with max VQ (higher than NS SVE VQ). */
+	rc = host_create_sve_realm_payload(true, sve_vq);
+	if (rc != TEST_RESULT_SUCCESS) {
+		return rc;
+	}
+
+	/* 4. Call Realm to fill in Z registers */
+	realm_rc = host_enter_realm_execute(REALM_SVE_FILL_REGS, NULL,
+					    RMI_EXIT_HOST_CALL);
+	if (!realm_rc) {
+		rc = TEST_RESULT_FAIL;
+		goto rm_realm;
+	}
+
+	/* 5. NS sets ZCR_EL2 with max VQ and reads the Z registers */
+	sve_config_vq(sve_vq);
+	sve_read_vector_regs(ns_sve_vectors_read);
+
+	/*
+	 * 6. The upper bits in Z vectors (sve_vq - SVE_VQ_ARCH_MIN) must
+	 *    be either 0 or the old values filled by NS world.
+	 *    TODO: check if upper bits are zero
+	 */
+	regs_base_wr = (uint8_t *)&ns_sve_vectors_write;
+	regs_base_rd = (uint8_t *)&ns_sve_vectors_read;
+
+	rc = TEST_RESULT_SUCCESS;
+	for (int i = 0U; i < SVE_NUM_VECTORS; i++) {
+		if (memcmp(regs_base_wr + (i * SVE_VQ_TO_BYTES(sve_vq)),
+			   regs_base_rd + (i * SVE_VQ_TO_BYTES(sve_vq)),
+			   SVE_VQ_TO_BYTES(sve_vq)) != 0) {
+			ERROR("SVE Z%d mismatch\n", i);
+			rc = TEST_RESULT_FAIL;
+		}
+	}
+
+rm_realm:
+	if (!host_destroy_realm()) {
+		return TEST_RESULT_FAIL;
+	}
+
+	return rc;
+}
diff --git a/tftf/tests/tests-realm-payload.xml b/tftf/tests/tests-realm-payload.xml
index 80d498d..6569357 100644
--- a/tftf/tests/tests-realm-payload.xml
+++ b/tftf/tests/tests-realm-payload.xml
@@ -49,5 +49,7 @@
 	  function="host_sve_realm_check_config_register" />
 	  <testcase name="Intermittently switch to Realm while doing NS SVE ops"
 	  function="host_sve_realm_check_vectors_operations" />
+	  <testcase name="Check if RMM does not leak Realm SVE vector registers"
+	  function="host_sve_realm_check_vectors_leaked" />
   </testsuite>
 </testsuites>