feat(cpufeat): do feature detection on secondary cores too

Feature detection currently only happens on the boot core, however, it
is possible to have asymmetry between cores. TF-A supports limited such
configurations so it should check secondary cores too.

Change-Id: Iee4955714685be9ae6a017af4a6c284e835ff299
Signed-off-by: Boyan Karatotev <boyan.karatotev@arm.com>
diff --git a/bl31/bl31_main.c b/bl31/bl31_main.c
index 4d641d3..9d300b4 100644
--- a/bl31/bl31_main.c
+++ b/bl31/bl31_main.c
@@ -120,8 +120,10 @@
  ******************************************************************************/
 void bl31_main(void)
 {
+	unsigned int core_pos = plat_my_core_pos();
+
 	/* Init registers that never change for the lifetime of TF-A */
-	cm_manage_extensions_el3(plat_my_core_pos());
+	cm_manage_extensions_el3(core_pos);
 
 	/* Init per-world context registers */
 	cm_manage_extensions_per_world();
@@ -131,7 +133,7 @@
 
 #if FEATURE_DETECTION
 	/* Detect if features enabled during compilation are supported by PE. */
-	detect_arch_features();
+	detect_arch_features(core_pos);
 #endif /* FEATURE_DETECTION */
 
 #if ENABLE_RUNTIME_INSTRUMENTATION
@@ -156,8 +158,6 @@
 	 * Initialize the GIC driver as well as per-cpu and global interfaces.
 	 * Platform has had an opportunity to initialise specifics.
 	 */
-	unsigned int core_pos = plat_my_core_pos();
-
 	gic_init(core_pos);
 	gic_pcpu_init(core_pos);
 	gic_cpuif_enable(core_pos);
diff --git a/common/feat_detect.c b/common/feat_detect.c
index d7adae6..694d616 100644
--- a/common/feat_detect.c
+++ b/common/feat_detect.c
@@ -7,8 +7,9 @@
 #include <arch_features.h>
 #include <common/debug.h>
 #include <common/feat_detect.h>
+#include <plat/common/platform.h>
 
-static bool tainted;
+static bool detection_done[PLATFORM_CORE_COUNT] = { false };
 
 /*******************************************************************************
  * This section lists the wrapper modules for each feature to evaluate the
@@ -45,19 +46,21 @@
  * We force inlining here to let the compiler optimise away the whole check
  * if the feature is disabled at build time (FEAT_STATE_DISABLED).
  ******************************************************************************/
-static inline void __attribute((__always_inline__))
+static inline bool __attribute((__always_inline__))
 check_feature(int state, unsigned long field, const char *feat_name,
 	      unsigned int min, unsigned int max)
 {
 	if (state == FEAT_STATE_ALWAYS && field < min) {
 		ERROR("FEAT_%s not supported by the PE\n", feat_name);
-		tainted = true;
+		return true;
 	}
 	if (state >= FEAT_STATE_ALWAYS && field > max) {
 		ERROR("FEAT_%s is version %ld, but is only known up to version %d\n",
 		      feat_name, field, max);
-		tainted = true;
+		return true;
 	}
+
+	return false;
 }
 
 /************************************************
@@ -326,127 +329,150 @@
  * { FEAT_STATE_DISABLED, FEAT_STATE_ALWAYS, FEAT_STATE_CHECK }, taking values
  * { 0, 1, 2 }, respectively, as their naming.
  **********************************************************************************/
-void detect_arch_features(void)
+void detect_arch_features(unsigned int core_pos)
 {
-	tainted = false;
+	/* No need to keep checking after the first time for each core. */
+	if (detection_done[core_pos]) {
+		return;
+	}
+
+	bool tainted = false;
 
 	/* v8.0 features */
-	check_feature(ENABLE_FEAT_SB, read_feat_sb_id_field(), "SB", 1, 1);
-	check_feature(ENABLE_FEAT_CSV2_2, read_feat_csv2_id_field(),
-		      "CSV2_2", 2, 3);
+	tainted |= check_feature(ENABLE_FEAT_SB, read_feat_sb_id_field(),
+				 "SB", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_CSV2_2, read_feat_csv2_id_field(),
+				 "CSV2_2", 2, 3);
 	/*
 	 * Even though the PMUv3 is an OPTIONAL feature, it is always
 	 * implemented and Arm prescribes so. So assume it will be there and do
 	 * away with a flag for it. This is used to check minor PMUv3px
 	 * revisions so that we catch them as they come along
 	 */
-	check_feature(FEAT_STATE_ALWAYS, read_feat_pmuv3_id_field(),
-		      "PMUv3", 1, ID_AA64DFR0_PMUVER_PMUV3P9);
+	tainted |= check_feature(FEAT_STATE_ALWAYS, read_feat_pmuv3_id_field(),
+				 "PMUv3", 1, ID_AA64DFR0_PMUVER_PMUV3P9);
 
 	/* v8.1 features */
-	check_feature(ENABLE_FEAT_PAN, read_feat_pan_id_field(), "PAN", 1, 3);
-	check_feature(ENABLE_FEAT_VHE, read_feat_vhe_id_field(), "VHE", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_PAN, read_feat_pan_id_field(),
+				 "PAN", 1, 3);
+	tainted |= check_feature(ENABLE_FEAT_VHE, read_feat_vhe_id_field(),
+				 "VHE", 1, 1);
 
 	/* v8.2 features */
-	check_feature(ENABLE_SVE_FOR_NS, read_feat_sve_id_field(),
-		      "SVE", 1, 1);
-	check_feature(ENABLE_FEAT_RAS, read_feat_ras_id_field(), "RAS", 1, 2);
+	tainted |= check_feature(ENABLE_SVE_FOR_NS, read_feat_sve_id_field(),
+				 "SVE", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_RAS, read_feat_ras_id_field(),
+				 "RAS", 1, 2);
 
 	/* v8.3 features */
 	/* TODO: Pauth yet to convert to tri-state feat detect logic */
 	read_feat_pauth();
 
 	/* v8.4 features */
-	check_feature(ENABLE_FEAT_DIT, read_feat_dit_id_field(), "DIT", 1, 1);
-	check_feature(ENABLE_FEAT_AMU, read_feat_amu_id_field(),
-		      "AMUv1", 1, 2);
-	check_feature(ENABLE_FEAT_MOPS, read_feat_mops_id_field(),
-		      "MOPS", 1, 1);
-	check_feature(ENABLE_FEAT_MPAM, read_feat_mpam_version(),
-		      "MPAM", 1, 17);
-	check_feature(CTX_INCLUDE_NEVE_REGS, read_feat_nv_id_field(),
-		      "NV2", 2, 2);
-	check_feature(ENABLE_FEAT_SEL2, read_feat_sel2_id_field(),
-		      "SEL2", 1, 1);
-	check_feature(ENABLE_TRF_FOR_NS, read_feat_trf_id_field(),
-		      "TRF", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_DIT, read_feat_dit_id_field(),
+				 "DIT", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_AMU, read_feat_amu_id_field(),
+				 "AMUv1", 1, 2);
+	tainted |= check_feature(ENABLE_FEAT_MOPS, read_feat_mops_id_field(),
+				 "MOPS", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_MPAM, read_feat_mpam_version(),
+				 "MPAM", 1, 17);
+	tainted |= check_feature(CTX_INCLUDE_NEVE_REGS, read_feat_nv_id_field(),
+				 "NV2", 2, 2);
+	tainted |= check_feature(ENABLE_FEAT_SEL2, read_feat_sel2_id_field(),
+				 "SEL2", 1, 1);
+	tainted |= check_feature(ENABLE_TRF_FOR_NS, read_feat_trf_id_field(),
+				 "TRF", 1, 1);
 
 	/* v8.5 features */
-	check_feature(ENABLE_FEAT_MTE2, get_armv8_5_mte_support(), "MTE2",
-		      MTE_IMPLEMENTED_ELX, MTE_IMPLEMENTED_ASY);
-	check_feature(ENABLE_FEAT_RNG, read_feat_rng_id_field(), "RNG", 1, 1);
-	check_feature(ENABLE_BTI, read_feat_bti_id_field(), "BTI", 1, 1);
-	check_feature(ENABLE_FEAT_RNG_TRAP, read_feat_rng_trap_id_field(),
-		      "RNG_TRAP", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_MTE2, get_armv8_5_mte_support(),
+				 "MTE2", MTE_IMPLEMENTED_ELX, MTE_IMPLEMENTED_ASY);
+	tainted |= check_feature(ENABLE_FEAT_RNG, read_feat_rng_id_field(),
+				 "RNG", 1, 1);
+	tainted |= check_feature(ENABLE_BTI, read_feat_bti_id_field(),
+				 "BTI", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_RNG_TRAP, read_feat_rng_trap_id_field(),
+				 "RNG_TRAP", 1, 1);
 
 	/* v8.6 features */
-	check_feature(ENABLE_FEAT_AMUv1p1, read_feat_amu_id_field(),
-		      "AMUv1p1", 2, 2);
-	check_feature(ENABLE_FEAT_FGT, read_feat_fgt_id_field(), "FGT", 1, 2);
-	check_feature(ENABLE_FEAT_FGT2, read_feat_fgt_id_field(), "FGT2", 2, 2);
-	check_feature(ENABLE_FEAT_ECV, read_feat_ecv_id_field(), "ECV", 1, 2);
-	check_feature(ENABLE_FEAT_TWED, read_feat_twed_id_field(),
-		      "TWED", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_AMUv1p1, read_feat_amu_id_field(),
+				 "AMUv1p1", 2, 2);
+	tainted |= check_feature(ENABLE_FEAT_FGT, read_feat_fgt_id_field(),
+				 "FGT", 1, 2);
+	tainted |= check_feature(ENABLE_FEAT_FGT2, read_feat_fgt_id_field(),
+				 "FGT2", 2, 2);
+	tainted |= check_feature(ENABLE_FEAT_ECV, read_feat_ecv_id_field(),
+				 "ECV", 1, 2);
+	tainted |= check_feature(ENABLE_FEAT_TWED, read_feat_twed_id_field(),
+				 "TWED", 1, 1);
 
 	/*
 	 * even though this is a "DISABLE" it does confusingly perform feature
 	 * enablement duties like all other flags here. Check it against the HW
 	 * feature when we intend to diverge from the default behaviour
 	 */
-	check_feature(DISABLE_MTPMU, read_feat_mtpmu_id_field(), "MTPMU", 1, 1);
+	tainted |= check_feature(DISABLE_MTPMU, read_feat_mtpmu_id_field(),
+				 "MTPMU", 1, 1);
 
 	/* v8.7 features */
-	check_feature(ENABLE_FEAT_HCX, read_feat_hcx_id_field(), "HCX", 1, 1);
-	check_feature(ENABLE_FEAT_LS64_ACCDATA, read_feat_ls64_id_field(), "LS64", 1, 3);
+	tainted |= check_feature(ENABLE_FEAT_HCX, read_feat_hcx_id_field(),
+				 "HCX", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_LS64_ACCDATA, read_feat_ls64_id_field(),
+				 "LS64", 1, 3);
 
 	/* v8.9 features */
-	check_feature(ENABLE_FEAT_TCR2, read_feat_tcr2_id_field(),
-		      "TCR2", 1, 1);
-	check_feature(ENABLE_FEAT_S2PIE, read_feat_s2pie_id_field(),
-		      "S2PIE", 1, 1);
-	check_feature(ENABLE_FEAT_S1PIE, read_feat_s1pie_id_field(),
-		      "S1PIE", 1, 1);
-	check_feature(ENABLE_FEAT_S2POE, read_feat_s2poe_id_field(),
-		      "S2POE", 1, 1);
-	check_feature(ENABLE_FEAT_S1POE, read_feat_s1poe_id_field(),
-		      "S1POE", 1, 1);
-	check_feature(ENABLE_FEAT_CSV2_3, read_feat_csv2_id_field(),
-		      "CSV2_3", 3, 3);
-	check_feature(ENABLE_FEAT_DEBUGV8P9, read_feat_debugv8p9_id_field(),
-			"DEBUGV8P9", 11, 11);
-	check_feature(ENABLE_FEAT_THE, read_feat_the_id_field(),
-			"THE", 1, 1);
-	check_feature(ENABLE_FEAT_SCTLR2, read_feat_sctlr2_id_field(),
-			"SCTLR2", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_TCR2, read_feat_tcr2_id_field(),
+				 "TCR2", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_S2PIE, read_feat_s2pie_id_field(),
+				 "S2PIE", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_S1PIE, read_feat_s1pie_id_field(),
+				 "S1PIE", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_S2POE, read_feat_s2poe_id_field(),
+				 "S2POE", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_S1POE, read_feat_s1poe_id_field(),
+				 "S1POE", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_CSV2_3, read_feat_csv2_id_field(),
+				 "CSV2_3", 3, 3);
+	tainted |= check_feature(ENABLE_FEAT_DEBUGV8P9, read_feat_debugv8p9_id_field(),
+				 "DEBUGV8P9", 11, 11);
+	tainted |= check_feature(ENABLE_FEAT_THE, read_feat_the_id_field(),
+				 "THE", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_SCTLR2, read_feat_sctlr2_id_field(),
+				 "SCTLR2", 1, 1);
 
 	/* v9.0 features */
-	check_feature(ENABLE_BRBE_FOR_NS, read_feat_brbe_id_field(),
-		      "BRBE", 1, 2);
-	check_feature(ENABLE_TRBE_FOR_NS, read_feat_trbe_id_field(),
-		      "TRBE", 1, 1);
+	tainted |= check_feature(ENABLE_BRBE_FOR_NS, read_feat_brbe_id_field(),
+				 "BRBE", 1, 2);
+	tainted |= check_feature(ENABLE_TRBE_FOR_NS, read_feat_trbe_id_field(),
+				 "TRBE", 1, 1);
 
 	/* v9.2 features */
-	check_feature(ENABLE_SME_FOR_NS, read_feat_sme_id_field(),
-		      "SME", 1, 2);
-	check_feature(ENABLE_SME2_FOR_NS, read_feat_sme_id_field(),
-		      "SME2", 2, 2);
-	check_feature(ENABLE_FEAT_FPMR, read_feat_fpmr_id_field(),
-		      "FPMR", 1, 1);
+	tainted |= check_feature(ENABLE_SME_FOR_NS, read_feat_sme_id_field(),
+				 "SME", 1, 2);
+	tainted |= check_feature(ENABLE_SME2_FOR_NS, read_feat_sme_id_field(),
+				 "SME2", 2, 2);
+	tainted |= check_feature(ENABLE_FEAT_FPMR, read_feat_fpmr_id_field(),
+				 "FPMR", 1, 1);
 
 	/* v9.3 features */
-	check_feature(ENABLE_FEAT_D128, read_feat_d128_id_field(),
-		      "D128", 1, 1);
-	check_feature(ENABLE_FEAT_GCIE, read_feat_gcie_id_field(),
-		      "GCIE", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_D128, read_feat_d128_id_field(),
+				 "D128", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_GCIE, read_feat_gcie_id_field(),
+				 "GCIE", 1, 1);
 
 	/* v9.4 features */
-	check_feature(ENABLE_FEAT_GCS, read_feat_gcs_id_field(), "GCS", 1, 1);
-	check_feature(ENABLE_RME, read_feat_rme_id_field(), "RME", 1, 1);
-	check_feature(ENABLE_FEAT_PAUTH_LR, is_feat_pauth_lr_present(), "PAUTH_LR", 1, 1);
-	check_feature(ENABLE_FEAT_FGWTE3, read_feat_fgwte3_id_field(),
-		      "FGWTE3", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_GCS, read_feat_gcs_id_field(),
+				 "GCS", 1, 1);
+	tainted |= check_feature(ENABLE_RME, read_feat_rme_id_field(),
+				 "RME", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_PAUTH_LR, is_feat_pauth_lr_present(),
+				 "PAUTH_LR", 1, 1);
+	tainted |= check_feature(ENABLE_FEAT_FGWTE3, read_feat_fgwte3_id_field(),
+				 "FGWTE3", 1, 1);
 
 	if (tainted) {
 		panic();
 	}
+
+	detection_done[core_pos] = true;
 }
diff --git a/include/common/feat_detect.h b/include/common/feat_detect.h
index 18e6c42..04e4c02 100644
--- a/include/common/feat_detect.h
+++ b/include/common/feat_detect.h
@@ -8,7 +8,7 @@
 #define FEAT_DETECT_H
 
 /* Function Prototypes */
-void detect_arch_features(void);
+void detect_arch_features(unsigned int core_pos);
 
 /* Macro Definitions */
 #define FEAT_STATE_DISABLED		0
diff --git a/lib/psci/psci_common.c b/lib/psci/psci_common.c
index 08e27ac..bc1bad0 100644
--- a/lib/psci/psci_common.c
+++ b/lib/psci/psci_common.c
@@ -1004,6 +1004,11 @@
 	unsigned int parent_nodes[PLAT_MAX_PWR_LVL] = {0};
 	psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} };
 
+#if FEATURE_DETECTION
+	/* Detect if features enabled during compilation are supported by PE. */
+	detect_arch_features(cpu_idx);
+#endif /* FEATURE_DETECTION */
+
 	/* Init registers that never change for the lifetime of TF-A */
 	cm_manage_extensions_el3(cpu_idx);