fix(rme): enhance fpu state verification test

Select a random FPU FILL/CMP command to send to different security
states in a more effective way. If a FPU FILL command is issued to
Non-secure or Realm or Secure world, this change ensures that the next
command issued will be a FPU COMPARE command to a security state that is
different from the previous state.

Signed-off-by: Arunachalam Ganapathy <arunachalam.ganapathy@arm.com>
Change-Id: I861892f2c340bb259eafa14d2fb540645ee5396a
diff --git a/tftf/tests/runtime_services/realm_payload/host_realm_spm.c b/tftf/tests/runtime_services/realm_payload/host_realm_spm.c
index dbc7102..a187584 100644
--- a/tftf/tests/runtime_services/realm_payload/host_realm_spm.c
+++ b/tftf/tests/runtime_services/realm_payload/host_realm_spm.c
@@ -26,16 +26,12 @@
 static fpu_state_t ns_fpu_state_write;
 static fpu_state_t ns_fpu_state_read;
 
-typedef enum test_rl_sec_fp_cmd {
-	CMD_SIMD_NS_FILL = 0U,
-	CMD_SIMD_NS_CMP,
-	CMD_SIMD_RL_FILL,
-	CMD_SIMD_RL_CMP,
-	CMD_MAX_THREE_WORLD,
-	CMD_SIMD_SEC_FILL,
-	CMD_SIMD_SEC_CMP,
-	CMD_MAX_COUNT
-} realm_test_cmd_t;
+typedef enum security_state {
+	NONSECURE_WORLD = 0U,
+	REALM_WORLD,
+	SECURE_WORLD,
+	SECURITY_STATE_MAX
+} security_state_t;
 
 /*
  * This function helps to Initialise secure_mailbox, creates realm payload and
@@ -44,9 +40,6 @@
  */
 static test_result_t init_sp(void)
 {
-	/* Verify that FFA is there and that it has the correct version. */
-	SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 1);
-
 	if (!secure_mailbox_initialised) {
 		GET_TFTF_MAILBOX(mb);
 		CHECK_SPMC_TESTING_SETUP(1, 1, expected_sp_uuids);
@@ -57,22 +50,8 @@
 
 static test_result_t init_realm(void)
 {
-	u_register_t retrmm;
 	u_register_t rec_flag[1] = {RMI_RUNNABLE};
 
-	if (get_armv9_2_feat_rme_support() == 0U) {
-		return TEST_RESULT_SKIPPED;
-	}
-
-	retrmm = host_rmi_version();
-
-	/*
-	 * Skip test if RMM is TRP, TRP version is always null.
-	 */
-	if (retrmm == 0UL) {
-		return TEST_RESULT_SKIPPED;
-	}
-
 	/*
 	 * Initialise Realm payload
 	 */
@@ -205,6 +184,12 @@
 	struct ffa_value ret_values;
 	test_result_t res;
 
+	/* Verify RME is present and RMM is not TRP */
+	SKIP_TEST_IF_RME_NOT_SUPPORTED_OR_RMM_IS_TRP();
+
+	/* Verify that FFA is there and that it has the correct version. */
+	SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 1);
+
 	res = init_sp();
 	if (res != TEST_RESULT_SUCCESS) {
 		return res;
@@ -217,17 +202,17 @@
 
 	/* Enable trusted watchdog interrupt as IRQ in the secure side. */
 	if (!enable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
-		return TEST_RESULT_FAIL;
+		goto destroy_realm;
 	}
 
 	/*
 	 * Send a message to SP1 through direct messaging.
 	 */
-	ret_values = cactus_send_twdog_cmd(SENDER, RECEIVER, (REALM_TIME_SLEEP/2));
-
+	ret_values = cactus_send_twdog_cmd(SENDER, RECEIVER,
+					   (REALM_TIME_SLEEP/2));
 	if (!is_ffa_direct_response(ret_values)) {
 		ERROR("Expected a direct response for starting TWDOG timer\n");
-		return TEST_RESULT_FAIL;
+		goto destroy_realm;
 	}
 
 	/*
@@ -242,8 +227,7 @@
 	 */
 	if (!host_realm_handle_fiq_exit(realm_ptr, 0U)) {
 		ERROR("Trusted watchdog timer interrupt not fired\n");
-		host_destroy_realm();
-		return TEST_RESULT_FAIL;
+		goto destroy_realm;
 	}
 
 	/* Check for the last serviced secure virtual interrupt. */
@@ -252,18 +236,18 @@
 	if (!is_ffa_direct_response(ret_values)) {
 		ERROR("Expected a direct response for last serviced interrupt"
 			" command\n");
-		return TEST_RESULT_FAIL;
+		goto destroy_realm;
 	}
 
 	/* Make sure Trusted Watchdog timer interrupt was serviced*/
 	if (cactus_get_response(ret_values) != IRQ_TWDOG_INTID) {
 		ERROR("Trusted watchdog timer interrupt not serviced by SP\n");
-		return TEST_RESULT_FAIL;
+		goto destroy_realm;
 	}
 
 	/* Disable Trusted Watchdog interrupt. */
 	if (!disable_trusted_wdog_interrupt(SENDER, RECEIVER)) {
-		return TEST_RESULT_FAIL;
+		goto destroy_realm;
 	}
 
 	if (!host_destroy_realm()) {
@@ -272,93 +256,148 @@
 	}
 
 	return TEST_RESULT_SUCCESS;
+
+destroy_realm:
+	host_destroy_realm();
+	return TEST_RESULT_FAIL;
+}
+
+/* Choose a random security state that is different from the 'current' state */
+static security_state_t get_random_security_state(security_state_t current,
+						  bool is_sp_present)
+{
+	security_state_t next;
+
+	/*
+	 * 3 world config: Switch between NS world and Realm world as Secure
+	 * world is not enabled or SP is not loaded.
+	 */
+	if (!is_sp_present) {
+		if (current == NONSECURE_WORLD) {
+			return REALM_WORLD;
+		} else {
+			return NONSECURE_WORLD;
+		}
+	}
+
+	/*
+	 * 4 world config: Randomly select a security_state between Realm, NS
+	 * and Secure until the new state is not equal to the current state.
+	 */
+	while (true) {
+		next = rand() % SECURITY_STATE_MAX;
+		if (next == current) {
+			continue;
+		}
+
+		break;
+	}
+
+	return next;
 }
 
 /*
- * Test that FPU/SIMD state are preserved during a randomly context switch
- * between secure/non-secure/realm(R-EL1)worlds.
- * FPU/SIMD state consist of the 32 SIMD vectors, FPCR and FPSR registers,
- * the test runs for 1000 iterations with random combination of:
- * SECURE_FILL_FPU, SECURE_READ_FPU, REALM_FILL_FPU, REALM_READ_FPU,
- * NONSECURE_FILL_FPU, NONSECURE_READ_FPU commands,to test all possible situations
- * of synchronous context switch between worlds, while the content of those registers
- * is being used.
+ * Test whether FPU/SIMD state (32 SIMD vectors, FPCR and FPSR registers) are
+ * preserved during a random context switch between Secure/Non-Secure/Realm world
+ *
+ * Below steps are performed by this test:
+ *
+ * Init:
+ * Fill FPU registers with random values in
+ * 1. NS World (NS-EL2)
+ * 2. Realm world (R-EL1)
+ * 3. Secure world (S-EL1) (if SP loaded)
+ *
+ * Test loop:
+ *  security_state_next = get_random_security_state(current, is_sp_present)
+ *
+ *  switch to security_state_next
+ *    if (FPU registers read != last filled values)
+ *       break loop; return TC_FAIL
+ *
+ *    Fill FPU registers with new random values for the next comparison.
  */
 test_result_t host_realm_fpu_access_in_rl_ns_se(void)
 {
-	int cmd = -1, old_cmd  = -1, cmd_max;
+	security_state_t sec_state;
+	bool is_sp_present;
 	test_result_t res;
 
-	res = init_sp();
-	if (res != TEST_RESULT_SUCCESS) {
-		cmd_max = CMD_MAX_THREE_WORLD;
-	} else {
-		cmd_max = CMD_MAX_COUNT;
-		if (!fpu_fill_sec()) {
-			ERROR("fpu_fill_sec error\n");
-			return TEST_RESULT_FAIL;
-		}
-	}
+	/* Verify RME is present and RMM is not TRP */
+	SKIP_TEST_IF_RME_NOT_SUPPORTED_OR_RMM_IS_TRP();
+
+	/* Verify that FFA is there and that it has the correct version. */
+	SKIP_TEST_IF_FFA_VERSION_LESS_THAN(1, 1);
 
 	res = init_realm();
 	if (res != TEST_RESULT_SUCCESS) {
 		return res;
 	}
 
-	/*
-	 * Fill all 3 world's FPU/SIMD state regs with some known values in the
-	 * beginning to have something later to compare to.
-	 */
+	/* Fill FPU registers in Non-secure world */
 	fpu_state_write_rand(&ns_fpu_state_write);
+
+	/* Fill FPU registers in Realm world */
 	if (!fpu_fill_rl()) {
 		ERROR("fpu_fill_rl error\n");
 		goto destroy_realm;
 	}
+	sec_state = REALM_WORLD;
 
-	for (uint32_t i = 0; i < 1000; i++) {
-		cmd = rand() % cmd_max;
-		if ((cmd == old_cmd) || cmd == CMD_MAX_THREE_WORLD) {
-			continue;
+	/* Fill FPU registers in Secure world if present */
+	res = init_sp();
+	if (res == TEST_RESULT_SUCCESS) {
+		if (!fpu_fill_sec()) {
+			ERROR("fpu_fill_sec error\n");
+			goto destroy_realm;
 		}
-		old_cmd = cmd;
 
-		switch (cmd) {
-		case CMD_SIMD_NS_FILL:
-			/* Non secure world fill FPU state registers */
-			fpu_state_write_rand(&ns_fpu_state_write);
-			break;
-		case CMD_SIMD_NS_CMP:
-			/* Normal world verify its FPU state registers data */
+		sec_state = SECURE_WORLD;
+		is_sp_present = true;
+	} else {
+		is_sp_present = false;
+	}
+
+	for (uint32_t i = 0; i < 128; i++) {
+		sec_state = get_random_security_state(sec_state, is_sp_present);
+
+		switch (sec_state) {
+		case NONSECURE_WORLD:
+			/* NS world verify its FPU/SIMD state registers */
 			fpu_state_read(&ns_fpu_state_read);
 			if (fpu_state_compare(&ns_fpu_state_write,
 					      &ns_fpu_state_read)) {
 				ERROR("%s failed %d\n", __func__, __LINE__);
 				goto destroy_realm;
 			}
+
+			/* Fill FPU state with new random values in NS world */
+			fpu_state_write_rand(&ns_fpu_state_write);
 			break;
-		case CMD_SIMD_SEC_FILL:
-			/* secure world fill FPU/SIMD state registers */
-			if (!fpu_fill_sec()) {
-				goto destroy_realm;
-			}
-			break;
-		case CMD_SIMD_SEC_CMP:
-			/* Secure world verify its FPU/SIMD state registers data */
-			if (!fpu_cmp_sec()) {
-				goto destroy_realm;
-			}
-			break;
-		case CMD_SIMD_RL_FILL:
-			/* Realm R-EL1 world fill FPU/SIMD state registers */
-			if (!fpu_fill_rl()) {
-				goto destroy_realm;
-			}
-			break;
-		case CMD_SIMD_RL_CMP:
-			/* Realm R-EL1 world verify its FPU/SIMD state registers data */
+		case REALM_WORLD:
+			/* Realm world verify its FPU/SIMD state registers */
 			if (!fpu_cmp_rl()) {
 				goto destroy_realm;
 			}
+
+			/* Fill FPU state with new random values in Realm */
+			if (!fpu_fill_rl()) {
+				goto destroy_realm;
+			}
+
+			break;
+		case SECURE_WORLD:
+			/* Secure world verify its FPU/SIMD state registers */
+			if (!fpu_cmp_sec()) {
+				goto destroy_realm;
+			}
+
+			/* Fill FPU state with new random values in SP */
+			if (!fpu_fill_sec()) {
+				goto destroy_realm;
+
+			}
+
 			break;
 		default:
 			break;