Merge "TFTF: Fix maximum number of CPUs in DSU cluster"
diff --git a/include/lib/tftf_lib.h b/include/lib/tftf_lib.h
index c3ad105..f9d1a6e 100644
--- a/include/lib/tftf_lib.h
+++ b/include/lib/tftf_lib.h
@@ -208,7 +208,7 @@
 unsigned int tftf_is_rebooted(void);
 
 static inline unsigned int make_mpid(unsigned int clusterid,
-#if FVP_MAX_PE_PER_CPU > 1
+#if PLAT_MAX_PE_PER_CPU > 1
 				     unsigned int coreid,
 				     unsigned int threadid)
 #else
@@ -221,7 +221,7 @@
 	 */
 	if ((read_mpidr_el1() & MPIDR_MT_MASK) != 0)
 		return MPIDR_MT_MASK |
-#if FVP_MAX_PE_PER_CPU > 1
+#if PLAT_MAX_PE_PER_CPU > 1
 			((threadid & MPIDR_AFFLVL_MASK) << MPIDR_AFF0_SHIFT) |
 #endif
 			((coreid & MPIDR_AFFLVL_MASK) << MPIDR_AFF1_SHIFT)   |
diff --git a/plat/arm/fvp/include/platform_def.h b/plat/arm/fvp/include/platform_def.h
index 3abeb03..ed45642 100644
--- a/plat/arm/fvp/include/platform_def.h
+++ b/plat/arm/fvp/include/platform_def.h
@@ -160,6 +160,8 @@
 					 PLATFORM_CORE_COUNT)
 #define PLATFORM_MAX_AFFLVL		MPIDR_AFFLVL2
 
+#define PLAT_MAX_PE_PER_CPU		FVP_MAX_PE_PER_CPU
+
 /* TODO : Migrate complete TFTF from affinity level to power levels */
 #define PLAT_MAX_PWR_LEVEL		PLATFORM_MAX_AFFLVL
 #define PLAT_MAX_PWR_STATES_PER_LVL	2
diff --git a/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c b/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c
index 708ea13..f58b6e4 100644
--- a/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c
+++ b/tftf/tests/runtime_services/standard_service/psci/api_tests/affinity_info/test_psci_affinity_info.c
@@ -214,7 +214,7 @@
 	 * CPU on the platform. The PSCI implementation should ignore the
 	 * affinity 0 field.
 	 */
-#if FVP_MAX_PE_PER_CPU > 1
+#if PLAT_MAX_PE_PER_CPU > 1
 	target_mpid = make_mpid(cluster_id, 0, 0xE1);
 #else
 	target_mpid = make_mpid(cluster_id, 0xE1);
diff --git a/tftf/tests/runtime_services/standard_service/sdei/system_tests/sdei_entrypoint.S b/tftf/tests/runtime_services/standard_service/sdei/system_tests/sdei_entrypoint.S
index 655bb24..74fe4a6 100644
--- a/tftf/tests/runtime_services/standard_service/sdei/system_tests/sdei_entrypoint.S
+++ b/tftf/tests/runtime_services/standard_service/sdei/system_tests/sdei_entrypoint.S
@@ -12,6 +12,7 @@
 	.globl	sdei_entrypoint
 	.globl	sdei_entrypoint_resume
 	.globl	sdei_handler_done
+	.globl	sdei_rm_any_entrypoint
 
 	.local	event_handled
 	.comm	event_handled, PLATFORM_CORE_COUNT * 4, 8
@@ -115,6 +116,16 @@
 	b	.
 endfunc sdei_state_entrypoint
 
+func sdei_rm_any_entrypoint
+	stp	xzr, x30, [sp, #-16]!
+	bl	test_sdei_routing_any_handler
+	ldp	xzr, x30, [sp],#16
+	mov_imm	x0, SDEI_EVENT_COMPLETE
+	mov	x1, xzr
+	smc	#0
+	b	.
+endfunc sdei_rm_any_entrypoint
+
 #else /* AARCH32 */
 func sdei_entrypoint
 	/* SDEI is not supported on AArch32. */
@@ -135,4 +146,9 @@
 	/* SDEI is not supported on AArch32. */
 	b	.
 endfunc sdei_state_entrypoint
+
+func sdei_rm_any_entrypoint
+	/* SDEI is not supported on AArch32. */
+	b	.
+endfunc sdei_rm_any_entrypoint
 #endif
diff --git a/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_rm_any.c b/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_rm_any.c
new file mode 100644
index 0000000..c8a8a41
--- /dev/null
+++ b/tftf/tests/runtime_services/standard_service/sdei/system_tests/test_sdei_rm_any.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 2020, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <debug.h>
+#include <events.h>
+#include <plat_topology.h>
+#include <platform.h>
+#include <power_management.h>
+#include <sdei.h>
+#include <timer.h>
+#include <tftf_lib.h>
+
+/* This test makes sure RM_ANY can route SDEI events to all cores. */
+
+extern sdei_handler_t sdei_rm_any_entrypoint;
+
+typedef enum {
+	CORE_STATUS_OFF = 0,
+	CORE_STATUS_READY,
+	CORE_STATUS_TRIGGERED
+} core_status_et;
+
+#define MPID_WAITING U(0xFFFFFFFF)
+
+/*
+ * These state variables are updated only by the lead CPU but are globals since
+ * the lead CPU can change and also the event handler needs access to some of
+ * them.
+ */
+static struct sdei_intr_ctx intr_ctx;
+static volatile u_register_t mpid_lead;
+static volatile int event;
+static volatile unsigned int event_count;
+static volatile unsigned int core_count;
+
+/* These are shared variables that are written to by the event handler. */
+static event_t exit_handler_event;
+static volatile u_register_t mpid_last_handler;
+static volatile core_status_et core_status[PLATFORM_CORE_COUNT];
+
+/* Clean up on lead CPU after test completes or fails. */
+static test_result_t cleanup(test_result_t result)
+{
+	long long ret;
+
+	/*
+	 * Sanity check that final event counter and core count match. If
+	 * somehow a single event gets triggered on multiple cores these
+	 * values will not match.
+	 */
+	if ((result == TEST_RESULT_SUCCESS) && (event_count != core_count)) {
+		printf("Event count (%u) and core count (%u) mismatch!",
+			event_count, core_count);
+		result = TEST_RESULT_FAIL;
+	}
+
+	/* Unregister SDEI event. */
+	ret = sdei_event_unregister(event);
+	if (ret < 0) {
+		printf("%u: %s failed (%lld)\n", __LINE__,
+			"sdei_event_unregister", ret);
+		result = TEST_RESULT_FAIL;
+	}
+
+	/* Unbind interrupt. */
+	ret = sdei_interrupt_release(event, &intr_ctx);
+	if (ret < 0) {
+		printf("%u: %s failed (%lld)\n", __LINE__,
+			"sdei_interrupt_release", ret);
+		result = TEST_RESULT_FAIL;
+	}
+	return result;
+}
+
+/* Lead CPU selects an heir before it powers off. */
+static test_result_t select_new_lead_cpu(void)
+{
+	/* Find a new lead CPU and update global. */
+	for (unsigned int i = 0U; i < core_count; i++) {
+		if (core_status[i] == CORE_STATUS_READY) {
+			mpid_lead = tftf_plat_get_mpidr(i);
+			return TEST_RESULT_SUCCESS;
+		}
+	}
+
+	/* Should never get here. */
+	return cleanup(TEST_RESULT_FAIL);
+}
+
+/* Lead CPU test manager. */
+static test_result_t lead_cpu_manage_test(u_register_t mpid)
+{
+	/* Loop until handler runs on lead CPU. */
+	while (mpid_last_handler != mpid_lead) {
+		/* Set up next event to trigger in 50ms. */
+		mpid_last_handler = MPID_WAITING;
+		tftf_program_timer(50U);
+		event_count++;
+
+		/* Wait for event to set MPID and cancel timer if needed. */
+		while (mpid_last_handler == MPID_WAITING) {
+			/* Nothing to do here just wait. */
+		}
+		if (mpid_last_handler != mpid_lead) {
+			tftf_cancel_timer();
+			tftf_send_event(&exit_handler_event);
+		}
+
+		/* Check state of CPU events. */
+		for (unsigned int i = 0U; i < core_count; i++) {
+			if (core_status[i] != CORE_STATUS_TRIGGERED) {
+				break;
+			}
+			if (i == (core_count - 1U)) {
+				/* Done when all cores triggered. */
+				return cleanup(TEST_RESULT_SUCCESS);
+			}
+		}
+	}
+
+	return select_new_lead_cpu();
+}
+
+/* All CPUs enter this function once test setup is done. */
+static test_result_t test_loop(void)
+{
+	/* Get affinity information. */
+	u_register_t mpid = read_mpidr_el1() & MPID_MASK;
+	unsigned int core_pos = platform_get_core_pos(mpid);
+
+	/* Unmask this CPU and mark it ready. */
+	sdei_pe_unmask();
+	core_status[core_pos] = CORE_STATUS_READY;
+
+	/* Wait for status change or to be promoted to leader. */
+	while (core_status[core_pos] != CORE_STATUS_TRIGGERED) {
+		if (mpid_lead == mpid) {
+			return lead_cpu_manage_test(mpid);
+		}
+	}
+
+	return TEST_RESULT_SUCCESS;
+}
+
+/* Called from ASM SDEI handler function. */
+void test_sdei_routing_any_handler(int ev, unsigned long long arg)
+{
+	/* Get affinity info. */
+	u_register_t mpid = read_mpidr_el1() & MPID_MASK;
+
+	/* Record event. */
+	printf("Event handled on CPU%u\n", platform_get_core_pos(mpid));
+	core_status[platform_get_core_pos(mpid)] = CORE_STATUS_TRIGGERED;
+	mpid_last_handler = mpid;
+
+	/*
+	 * Timer must be cancelled by the lead CPU before returning from
+	 * handler or the event will be triggered again.
+	 */
+	if (mpid == mpid_lead) {
+		tftf_cancel_timer();
+	} else {
+		tftf_wait_for_event(&exit_handler_event);
+	}
+}
+
+/* Lead CPU enters this function and sets up the test. */
+test_result_t test_sdei_routing_any(void)
+{
+	u_register_t target_mpid;
+	int cpu_node;
+	long long ret;
+
+	/* Set up test variables. */
+	mpid_lead = read_mpidr_el1() & MPID_MASK;
+	for (unsigned int i = 0U; i < PLATFORM_CORE_COUNT; i++) {
+		core_status[i] = CORE_STATUS_OFF;
+	}
+	core_status[platform_get_core_pos(mpid_lead)] = CORE_STATUS_READY;
+	event_count = 0U;
+	mpid_last_handler = MPID_WAITING;
+	tftf_init_event(&exit_handler_event);
+
+	/* Make sure SDEI is supported before performing test. */
+	ret = sdei_version();
+	if (ret != MAKE_SDEI_VERSION(1U, 0U, 0U)) {
+		printf("Unexpected SDEI version: 0x%llx\n", ret);
+		return TEST_RESULT_SKIPPED;
+	}
+
+	/* Initialize SDEI event to use TFTF timer as trigger. */
+	event = sdei_interrupt_bind(tftf_get_timer_irq(), &intr_ctx);
+	if (event < 0) {
+		printf("%u: %s failed (%d)\n", __LINE__,
+			"sdei_interrupt_bind", event);
+		return TEST_RESULT_FAIL;
+	}
+	ret = sdei_event_register(event, sdei_rm_any_entrypoint, 0U,
+			SDEI_REGF_RM_ANY, 0U);
+	if (ret < 0) {
+		printf("%u: %s failed (%lld)\n", __LINE__,
+			"sdei_event_register", ret);
+		return cleanup(TEST_RESULT_FAIL);
+	}
+	ret = sdei_event_enable(event);
+	if (ret < 0) {
+		printf("%u: %s failed (%lld)\n", __LINE__, "sdei_event_enable",
+			ret);
+		return cleanup(TEST_RESULT_FAIL);
+	}
+
+	/* Power on all CPUs and wait for them to be ready. */
+	printf("Powering up CPUs.\n");
+	core_count = 0U;
+	for_each_cpu(cpu_node) {
+		target_mpid = tftf_get_mpidr_from_node(cpu_node) & MPID_MASK;
+		if (mpid_lead != target_mpid) {
+			ret = tftf_cpu_on(target_mpid,
+				(uintptr_t)test_loop, 0U);
+			if (ret != PSCI_E_SUCCESS) {
+				printf("CPU ON failed for 0x%llx\n",
+					(unsigned long long)target_mpid);
+				return cleanup(TEST_RESULT_FAIL);
+			}
+		}
+		core_count++;
+	}
+	for (unsigned int i = 0U; i < core_count; i++) {
+		if (core_status[i] != CORE_STATUS_READY) {
+			i = 0U;
+		}
+	}
+
+	/* Cores are powered up and in the loop, enter loop function. */
+	printf("All CPUs ready, beginning test.\n");
+	return test_loop();
+}
diff --git a/tftf/tests/tests-sdei.mk b/tftf/tests/tests-sdei.mk
index 47bd506..0c495d9 100644
--- a/tftf/tests/tests-sdei.mk
+++ b/tftf/tests/tests-sdei.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2018, Arm Limited. All rights reserved.
+# Copyright (c) 2020, Arm Limited. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -9,4 +9,5 @@
 		sdei_entrypoint.S 						\
 		test_sdei.c 							\
 		test_sdei_state.c 						\
+		test_sdei_rm_any.c 						\
 	)
diff --git a/tftf/tests/tests-sdei.xml b/tftf/tests/tests-sdei.xml
index db6b0c9..147835b 100644
--- a/tftf/tests/tests-sdei.xml
+++ b/tftf/tests/tests-sdei.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
 <!--
-  Copyright (c) 2018, Arm Limited. All rights reserved.
+  Copyright (c) 2020, Arm Limited. All rights reserved.
 
   SPDX-License-Identifier: BSD-3-Clause
 -->
@@ -14,6 +14,7 @@
      <testcase name="SDEI event handling on all cores in parallel" function="test_sdei_event_parallel" />
      <testcase name="SDEI event signaling: each core signals itself" function="test_sdei_event_signal_serial" />
      <testcase name="SDEI event signaling: one core signals all others" function="test_sdei_event_signal_all" />
+     <testcase name="SDEI event routing all: SPI events routed to all CPUs" function="test_sdei_routing_any" />
   </testsuite>
 
 </testsuites>