feat(gcs): support guarded control stack

Arm v9.4 introduces support for Guarded Control Stack, providing
mitigations against some forms of RPO attacks and an efficient mechanism
for obtaining the current call stack without requiring a full stack
unwind. Enable access to this feature for EL2 and below, context
switching the newly added EL2 registers as appropriate.

Change the FVP platform to default to handling this as a dynamic option
so the right decision can be made by the code at runtime.

Signed-off-by: Mark Brown <broonie@kernel.org>
Change-Id: I691aa7c22e3547bb3abe98d96993baf18c5f0e7b
diff --git a/Makefile b/Makefile
index 98e448f..5306ddf 100644
--- a/Makefile
+++ b/Makefile
@@ -1189,6 +1189,7 @@
         ENABLE_FEAT_S1PIE \
         ENABLE_FEAT_S2POE \
         ENABLE_FEAT_S1POE \
+        ENABLE_FEAT_GCS \
         ENABLE_FEAT_VHE \
         ENABLE_MPAM_FOR_LOWER_ELS \
         ENABLE_RME \
@@ -1329,6 +1330,7 @@
         ENABLE_FEAT_S1PIE \
         ENABLE_FEAT_S2POE \
         ENABLE_FEAT_S1POE \
+        ENABLE_FEAT_GCS \
         FEATURE_DETECTION \
         TWED_DELAY \
         ENABLE_FEAT_TWED \
diff --git a/changelog.yaml b/changelog.yaml
index a514abd..2f51ce0 100644
--- a/changelog.yaml
+++ b/changelog.yaml
@@ -104,6 +104,9 @@
       - title: CPU feature / ID register handling in general
         scope: cpufeat
 
+      - title: Guarded Control Stack (FEAT_GCS)
+        scope: gcs
+
       - title: Support for the `HCRX_EL2` register (FEAT_HCX)
         scope: hcx
 
diff --git a/common/feat_detect.c b/common/feat_detect.c
index 1582b9d..98b420a 100644
--- a/common/feat_detect.c
+++ b/common/feat_detect.c
@@ -228,6 +228,10 @@
 	/* v9.2 features */
 	check_feature(ENABLE_SME_FOR_NS, read_feat_sme_id_field(),
 		      "SME", 1, 2);
+
+	/* v9.4 features */
+	check_feature(ENABLE_FEAT_GCS, read_feat_gcs_id_field(), "GCS", 1, 1);
+
 	read_feat_rme();
 
 	if (tainted) {
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index 03be786..d2f463f 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -374,6 +374,12 @@
    can take the values 0 to 2, to align  with the ``FEATURE_DETECTION``
    mechanism. Default value is ``0``.
 
+-  ``ENABLE_FEAT_GCS``: Numeric value to set the bit SCR_EL3.GCSEn in EL3 to
+   allow use of Guarded Control Stack from EL2 as well as adding the GCS
+   registers to the EL2 context save/restore operations. This flag can take
+   the values 0 to 2, to align  with the ``FEATURE_DETECTION`` mechanism.
+   Default value is ``0``.
+
 -  ``ENABLE_LTO``: Boolean option to enable Link Time Optimization (LTO)
    support in GCC for TF-A. This option is currently only supported for
    AArch64. Default is 0.
diff --git a/include/arch/aarch64/arch.h b/include/arch/aarch64/arch.h
index 89f4b40..9e061bf 100644
--- a/include/arch/aarch64/arch.h
+++ b/include/arch/aarch64/arch.h
@@ -371,6 +371,9 @@
 #define ID_AA64MMFR3_EL1_TCRX_MASK		ULL(0xf)
 
 /* ID_AA64PFR1_EL1 definitions */
+#define ID_AA64PFR1_EL1_GCS_SHIFT	U(44)
+#define ID_AA64PFR1_EL1_GCS_MASK	ULL(0xf)
+
 #define ID_AA64PFR1_EL1_SSBS_SHIFT	U(4)
 #define ID_AA64PFR1_EL1_SSBS_MASK	ULL(0xf)
 
@@ -527,6 +530,7 @@
 #define SCR_PIEN_BIT		(UL(1) << 45)
 #define SCR_TCR2EN_BIT		(UL(1) << 43)
 #define SCR_TRNDR_BIT		(UL(1) << 40)
+#define SCR_GCSEn_BIT		(UL(1) << 39)
 #define SCR_HXEn_BIT		(UL(1) << 38)
 #define SCR_ENTP2_SHIFT		U(41)
 #define SCR_ENTP2_BIT		(UL(1) << SCR_ENTP2_SHIFT)
@@ -1351,6 +1355,12 @@
 #define S2PIR_EL2		S3_4_C10_C2_5
 
 /*******************************************************************************
+ * FEAT_GCS - Guarded Control Stack Registers
+ ******************************************************************************/
+#define GCSCR_EL2		S3_4_C2_C5_0
+#define GCSPR_EL2		S3_4_C2_C5_1
+
+/*******************************************************************************
  * Definitions for DynamicIQ Shared Unit registers
  ******************************************************************************/
 #define CLUSTERPWRDN_EL1	S3_0_c15_c3_6
diff --git a/include/arch/aarch64/arch_features.h b/include/arch/aarch64/arch_features.h
index 840b117..16f4fb9 100644
--- a/include/arch/aarch64/arch_features.h
+++ b/include/arch/aarch64/arch_features.h
@@ -316,6 +316,24 @@
 	return is_feat_s1pie_supported() || is_feat_s2pie_supported();
 }
 
+static unsigned int read_feat_gcs_id_field(void)
+{
+	return ISOLATE_FIELD(read_id_aa64pfr1_el1(), ID_AA64PFR1_EL1_GCS);
+}
+
+static inline bool is_feat_gcs_supported(void)
+{
+	if (ENABLE_FEAT_GCS == FEAT_STATE_DISABLED) {
+		return false;
+	}
+
+	if (ENABLE_FEAT_GCS == FEAT_STATE_ALWAYS) {
+		return true;
+	}
+
+	return read_feat_gcs_id_field() != 0U;
+}
+
 /*******************************************************************************
  * Functions to identify the presence of the Activity Monitors Extension
  ******************************************************************************/
diff --git a/include/arch/aarch64/arch_helpers.h b/include/arch/aarch64/arch_helpers.h
index f877f5b..1b4bc11 100644
--- a/include/arch/aarch64/arch_helpers.h
+++ b/include/arch/aarch64/arch_helpers.h
@@ -613,6 +613,10 @@
 /* FEAT_SxPOE Registers */
 DEFINE_RENAME_SYSREG_RW_FUNCS(por_el2, POR_EL2)
 
+/* FEAT_GCS Registers */
+DEFINE_RENAME_SYSREG_RW_FUNCS(gcscr_el2, GCSCR_EL2)
+DEFINE_RENAME_SYSREG_RW_FUNCS(gcspr_el2, GCSPR_EL2)
+
 /* DynamIQ Shared Unit power management */
 DEFINE_RENAME_SYSREG_RW_FUNCS(clusterpwrdn_el1, CLUSTERPWRDN_EL1)
 
diff --git a/include/lib/el3_runtime/aarch64/context.h b/include/lib/el3_runtime/aarch64/context.h
index a8e6d8a..dd2b836 100644
--- a/include/lib/el3_runtime/aarch64/context.h
+++ b/include/lib/el3_runtime/aarch64/context.h
@@ -234,9 +234,11 @@
 #define CTX_PIRE0_EL2           U(0x1e8)
 #define CTX_PIR_EL2             U(0x1f0)
 #define CTX_S2PIR_EL2		U(0x1f8)
+#define CTX_GCSCR_EL2           U(0x200)
+#define CTX_GCSPR_EL2           U(0x208)
 
 /* Align to the next 16 byte boundary */
-#define CTX_EL2_SYSREGS_END	U(0x200)
+#define CTX_EL2_SYSREGS_END	U(0x210)
 
 #endif /* CTX_INCLUDE_EL2_REGS */
 
diff --git a/lib/el3_runtime/aarch64/context_mgmt.c b/lib/el3_runtime/aarch64/context_mgmt.c
index c411b73..e38b34d 100644
--- a/lib/el3_runtime/aarch64/context_mgmt.c
+++ b/lib/el3_runtime/aarch64/context_mgmt.c
@@ -376,6 +376,13 @@
 	}
 
 	/*
+	 * SCR_EL3.GCSEn: Enable GCS registers for AArch64 if present.
+	 */
+	if ((is_feat_gcs_supported()) && (GET_RW(ep->spsr) == MODE_RW_64)) {
+		scr_el3 |= SCR_GCSEn_BIT;
+	}
+
+	/*
 	 * CPTR_EL3 was initialized out of reset, copy that value to the
 	 * context register.
 	 */
@@ -1039,6 +1046,10 @@
 		if (is_feat_sxpoe_supported()) {
 			write_ctx_reg(el2_sysregs_ctx, CTX_POR_EL2, read_por_el2());
 		}
+		if (is_feat_gcs_supported()) {
+			write_ctx_reg(el2_sysregs_ctx, CTX_GCSPR_EL2, read_gcspr_el2());
+			write_ctx_reg(el2_sysregs_ctx, CTX_GCSCR_EL2, read_gcscr_el2());
+		}
 	}
 }
 
@@ -1116,6 +1127,10 @@
 		if (is_feat_sxpoe_supported()) {
 			write_por_el2(read_ctx_reg(el2_sysregs_ctx, CTX_POR_EL2));
 		}
+		if (is_feat_gcs_supported()) {
+			write_gcscr_el2(read_ctx_reg(el2_sysregs_ctx, CTX_GCSCR_EL2));
+			write_gcspr_el2(read_ctx_reg(el2_sysregs_ctx, CTX_GCSPR_EL2));
+		}
 	}
 }
 #endif /* CTX_INCLUDE_EL2_REGS */
diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk
index 808a058..34a9bc6 100644
--- a/make_helpers/defaults.mk
+++ b/make_helpers/defaults.mk
@@ -188,6 +188,9 @@
 # Flag to enable access to Stage 1 Permission Overlay (FEAT_S1POE)
 ENABLE_FEAT_S1POE		:= 0
 
+# Flag to enable access to Guarded Control Stack (FEAT_GCS)
+ENABLE_FEAT_GCS			:= 0
+
 # By default BL31 encryption disabled
 ENCRYPT_BL31			:= 0
 
diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk
index 3fb323b..e81f1eb 100644
--- a/plat/arm/board/fvp/platform.mk
+++ b/plat/arm/board/fvp/platform.mk
@@ -49,6 +49,7 @@
 	ENABLE_MPAM_FOR_LOWER_ELS	:= 2
 	ENABLE_FEAT_RNG			:= 2
 	ENABLE_FEAT_TWED		:= 2
+	ENABLE_FEAT_GCS			:= 2
 ifeq (${ARCH},aarch64)
 ifeq (${SPM_MM}, 0)
 ifeq (${ENABLE_RME}, 0)