feat(cpufeat): add support for FEAT_FGWTE3

Enable write traps for key EL3 system registers as per FEAT_FGWTE3,
ensuring their values remain unchanged after boot.

Excluded Registers:
MDCR_EL3 and MPAM3_EL3: Not trapped as they are part of the EL3 context.
SCTLR_EL3: Not trapped since it is overwritten during
powerdown sequence(Included when HW_ASSISTED_COHERENCY=1)

TPIDR_EL3: Excluded due to its use in crash reporting(It is included
when CRASH_REPORTING=0)

Reference:
https://developer.arm.com/documentation/ddi0601/2025-06/AArch64-Registers/FGWTE3-EL3--Fine-Grained-Write-Traps-EL3

Change-Id: Idcb32aaac7d65a0b0e5c90571af00e01a4e9edb1
Signed-off-by: Arvind Ram Prakash <arvind.ramprakash@arm.com>
diff --git a/Makefile b/Makefile
index 386a74b..e0b7acc 100644
--- a/Makefile
+++ b/Makefile
@@ -971,6 +971,7 @@
 	ENABLE_FEAT_ECV \
 	ENABLE_FEAT_FGT \
 	ENABLE_FEAT_FGT2 \
+	ENABLE_FEAT_FGWTE3 \
 	ENABLE_FEAT_FPMR \
 	ENABLE_FEAT_HCX \
 	ENABLE_FEAT_LS64_ACCDATA \
@@ -1141,6 +1142,7 @@
 	FEAT_PABANDON \
 	ENABLE_FEAT_FGT \
 	ENABLE_FEAT_FGT2 \
+	ENABLE_FEAT_FGWTE3 \
 	ENABLE_FEAT_FPMR \
 	ENABLE_FEAT_ECV \
 	ENABLE_FEAT_AMUv1p1 \
diff --git a/common/feat_detect.c b/common/feat_detect.c
index fa817d9..d7adae6 100644
--- a/common/feat_detect.c
+++ b/common/feat_detect.c
@@ -297,6 +297,12 @@
 			     ID_AA64ISAR2_EL1_MOPS_MASK);
 }
 
+static unsigned int read_feat_fgwte3_id_field(void)
+{
+	return ISOLATE_FIELD(read_id_aa64mmfr4_el1(), ID_AA64MMFR4_EL1_FGWTE3_SHIFT,
+			     ID_AA64MMFR4_EL1_FGWTE3_MASK);
+}
+
 /***********************************************************************************
  * TF-A supports many Arm architectural features starting from arch version
  * (8.0 till 8.7+). These features are mostly enabled through build flags. This
@@ -437,6 +443,8 @@
 	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);
 
 	if (tainted) {
 		panic();
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index f99840b..8a607ac 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -375,6 +375,23 @@
    This flag can take the values 0 to 2, to align  with the ``ENABLE_FEAT``
    mechanism. Default value is ``0``.
 
+-  ``ENABLE_FEAT_FGWTE3``: Numeric value to enable support for
+   Fine Grained Write Trap EL3 (FEAT_FGWTE3), a feature that allows EL3 to
+   restrict overwriting certain EL3 registers after boot.
+   This lockdown is established by setting individual trap bits for
+   system registers that are not expected to be overwritten after boot.
+   This feature is an optional architectural feature and is available from
+   Armv9.4 onwards. This flag can take values from 0 to 2, aligning with
+   the ``ENABLE_FEAT`` mechanism. The default value is 0.
+
+   .. note::
+      This feature currently traps access to all EL3 registers in
+      ``FGWTE3_EL3``, except for ``MDCR_EL3``, ``MPAM3_EL3``,
+      ``TPIDR_EL3``(when ``CRASH_REPORTING=1``), and
+      ``SCTLR_EL3``(when ``HW_ASSISTED_COHERENCY=0``).
+      If additional traps need to be disabled for specific platforms,
+      please contact the Arm team on `TF-A public mailing list`_.
+
 -  ``ENABLE_FEAT_HCX``: Numeric value to set the bit SCR_EL3.HXEn in EL3 to
    allow access to HCRX_EL2 (extended hypervisor control register) from EL2 as
    well as adding HCRX_EL2 to the EL2 context save/restore operations. Its a
@@ -1567,3 +1584,4 @@
 .. _Firmware Handoff specification: https://github.com/FirmwareHandoff/firmware_handoff/releases/tag/v0.9
 .. _PSA Crypto API specification: https://armmbed.github.io/mbed-crypto/html/
 .. _Platform Initialization specification: https://uefi.org/specs/PI/1.8/index.html
+.. _TF-A public mailing list: https://lists.trustedfirmware.org/mailman3/lists/tf-a.lists.trustedfirmware.org/
diff --git a/include/arch/aarch64/arch.h b/include/arch/aarch64/arch.h
index 5506cb1..ee18309 100644
--- a/include/arch/aarch64/arch.h
+++ b/include/arch/aarch64/arch.h
@@ -443,6 +443,13 @@
 #define ID_AA64MMFR3_EL1_TCRX_SHIFT		U(0)
 #define ID_AA64MMFR3_EL1_TCRX_MASK		ULL(0xf)
 
+/* ID_AA64MMFR4_EL1 definitions */
+#define ID_AA64MMFR4_EL1			S3_0_C0_C7_4
+
+#define ID_AA64MMFR4_EL1_FGWTE3_SHIFT		U(16)
+#define ID_AA64MMFR4_EL1_FGWTE3_MASK		ULL(0xf)
+#define FGWTE3_IMPLEMENTED			ULL(0x1)
+
 /* ID_AA64PFR1_EL1 definitions */
 
 #define ID_AA64PFR1_EL1_BT_SHIFT	U(0)
@@ -1622,4 +1629,68 @@
 #define MECIDR_EL2_MECIDWidthm1_MASK	U(0xf)
 #define MECIDR_EL2_MECIDWidthm1_SHIFT	U(0)
 
+/******************************************************************************
+ * FEAT_FGWTE3 - Fine Grained Write Trap
+ ******************************************************************************/
+#define FGWTE3_EL3					S3_6_C1_C1_5
+
+/* FGWTE3_EL3 Defintions */
+#define FGWTE3_EL3_VBAR_EL3_BIT				(U(1) << 21)
+#define FGWTE3_EL3_TTBR0_EL3_BIT			(U(1) << 20)
+#define FGWTE3_EL3_TPIDR_EL3_BIT			(U(1) << 19)
+#define FGWTE3_EL3_TCR_EL3_BIT				(U(1) << 18)
+#define FGWTE3_EL3_SPMROOTCR_EL3_BIT			(U(1) << 17)
+#define FGWTE3_EL3_SCTLR2_EL3_BIT			(U(1) << 16)
+#define FGWTE3_EL3_SCTLR_EL3_BIT			(U(1) << 15)
+#define FGWTE3_EL3_PIR_EL3_BIT				(U(1) << 14)
+#define FGWTE3_EL3_MECID_RL_A_EL3_BIT			(U(1) << 12)
+#define FGWTE3_EL3_MAIR2_EL3_BIT			(U(1) << 10)
+#define FGWTE3_EL3_MAIR_EL3_BIT				(U(1) << 9)
+#define FGWTE3_EL3_GPTBR_EL3_BIT			(U(1) << 8)
+#define FGWTE3_EL3_GPCCR_EL3_BIT			(U(1) << 7)
+#define FGWTE3_EL3_GCSPR_EL3_BIT			(U(1) << 6)
+#define FGWTE3_EL3_GCSCR_EL3_BIT			(U(1) << 5)
+#define FGWTE3_EL3_AMAIR2_EL3_BIT			(U(1) << 4)
+#define FGWTE3_EL3_AMAIR_EL3_BIT			(U(1) << 3)
+#define FGWTE3_EL3_AFSR1_EL3_BIT			(U(1) << 2)
+#define FGWTE3_EL3_AFSR0_EL3_BIT			(U(1) << 1)
+#define FGWTE3_EL3_ACTLR_EL3_BIT			(U(1) << 0)
+
+#define FGWTE3_EL3_EARLY_INIT_VAL			(	\
+		FGWTE3_EL3_VBAR_EL3_BIT 		| 	\
+		FGWTE3_EL3_TTBR0_EL3_BIT 		|	\
+		FGWTE3_EL3_SPMROOTCR_EL3_BIT		|	\
+		FGWTE3_EL3_SCTLR2_EL3_BIT		|	\
+		FGWTE3_EL3_PIR_EL3_BIT			|	\
+		FGWTE3_EL3_MECID_RL_A_EL3_BIT		|	\
+		FGWTE3_EL3_MAIR2_EL3_BIT		|	\
+		FGWTE3_EL3_MAIR_EL3_BIT			|	\
+		FGWTE3_EL3_GPTBR_EL3_BIT		|	\
+		FGWTE3_EL3_GPCCR_EL3_BIT		|	\
+		FGWTE3_EL3_GCSPR_EL3_BIT		|	\
+		FGWTE3_EL3_GCSCR_EL3_BIT		|	\
+		FGWTE3_EL3_AMAIR2_EL3_BIT		|	\
+		FGWTE3_EL3_AMAIR_EL3_BIT		|	\
+		FGWTE3_EL3_AFSR1_EL3_BIT		|	\
+		FGWTE3_EL3_AFSR0_EL3_BIT)
+
+#if HW_ASSISTED_COHERENCY
+#define FGWTE3_EL3_LATE_INIT_SCTLR_EL3_BIT   FGWTE3_EL3_SCTLR_EL3_BIT |
+#else
+#define FGWTE3_EL3_LATE_INIT_SCTLR_EL3_BIT
+#endif
+
+#if !(CRASH_REPORTING)
+#define FGWTE3_EL3_LATE_INIT_TPIDR_EL3_BIT	FGWTE3_EL3_TPIDR_EL3_BIT |
+#else
+#define FGWTE3_EL3_LATE_INIT_TPIDR_EL3_BIT
+#endif
+
+#define FGWTE3_EL3_LATE_INIT_VAL			(	\
+		FGWTE3_EL3_EARLY_INIT_VAL		|	\
+		FGWTE3_EL3_LATE_INIT_SCTLR_EL3_BIT		\
+		FGWTE3_EL3_LATE_INIT_TPIDR_EL3_BIT		\
+		FGWTE3_EL3_TCR_EL3_BIT			|	\
+		FGWTE3_EL3_ACTLR_EL3_BIT)
+
 #endif /* ARCH_H */
diff --git a/include/arch/aarch64/arch_features.h b/include/arch/aarch64/arch_features.h
index 77355cd..ddb5e23 100644
--- a/include/arch/aarch64/arch_features.h
+++ b/include/arch/aarch64/arch_features.h
@@ -148,6 +148,8 @@
  * +----------------------------+
  * |	FEAT_PAUTH_LR		|
  * +----------------------------+
+ * |    FEAT_FGWTE3             |
+ * +----------------------------+
  */
 
 __attribute__((always_inline))
@@ -271,6 +273,11 @@
 CREATE_FEATURE_FUNCS(feat_fgt2, id_aa64mmfr0_el1, ID_AA64MMFR0_EL1_FGT_SHIFT,
 		     ID_AA64MMFR0_EL1_FGT_MASK, FGT2_IMPLEMENTED, ENABLE_FEAT_FGT2)
 
+/* FEAT_FGWTE3: Fine-grained write traps EL3 */
+CREATE_FEATURE_FUNCS(feat_fgwte3, id_aa64mmfr4_el1, ID_AA64MMFR4_EL1_FGWTE3_SHIFT,
+		     ID_AA64MMFR4_EL1_FGWTE3_MASK, FGWTE3_IMPLEMENTED,
+		     ENABLE_FEAT_FGWTE3)
+
 /* FEAT_ECV: Enhanced Counter Virtualization */
 CREATE_FEATURE_FUNCS(feat_ecv, id_aa64mmfr0_el1, ID_AA64MMFR0_EL1_ECV_SHIFT,
 		     ID_AA64MMFR0_EL1_ECV_MASK, 1U, ENABLE_FEAT_ECV)
diff --git a/include/arch/aarch64/arch_helpers.h b/include/arch/aarch64/arch_helpers.h
index a59c531..b0ad93b 100644
--- a/include/arch/aarch64/arch_helpers.h
+++ b/include/arch/aarch64/arch_helpers.h
@@ -770,6 +770,11 @@
 /* FEAT_MEC Registers */
 DEFINE_RENAME_SYSREG_READ_FUNC(mecidr_el2, MECIDR_EL2)
 
+DEFINE_RENAME_IDREG_READ_FUNC(id_aa64mmfr4_el1, ID_AA64MMFR4_EL1)
+
+/* FEAT_FGWTE3 Registers */
+DEFINE_RENAME_SYSREG_RW_FUNCS(fgwte3_el3, FGWTE3_EL3)
+
 #define IS_IN_EL(x) \
 	(GET_EL(read_CurrentEl()) == MODE_EL##x)
 
diff --git a/lib/el3_runtime/aarch64/context_mgmt.c b/lib/el3_runtime/aarch64/context_mgmt.c
index a178f48..e188f74 100644
--- a/lib/el3_runtime/aarch64/context_mgmt.c
+++ b/lib/el3_runtime/aarch64/context_mgmt.c
@@ -671,6 +671,9 @@
 		sme_init_el3();
 	}
 
+	if (is_feat_fgwte3_supported()) {
+		write_fgwte3_el3(FGWTE3_EL3_EARLY_INIT_VAL);
+	}
 	pmuv3_init_el3();
 }
 
@@ -1161,6 +1164,14 @@
 				init_nonsecure_el2_unused(ctx);
 			}
 		}
+
+		if (is_feat_fgwte3_supported()) {
+			/*
+			 * TCR_EL3 and ACTLR_EL3 could be overwritten
+			 * by platforms and hence is locked a bit late.
+			 */
+			write_fgwte3_el3(FGWTE3_EL3_LATE_INIT_VAL);
+		}
 	}
 #if (!CTX_INCLUDE_EL2_REGS)
 	/* Restore EL1 system registers, only when CTX_INCLUDE_EL2_REGS=0 */
diff --git a/make_helpers/arch_features.mk b/make_helpers/arch_features.mk
index a6fd6be..8bcf866 100644
--- a/make_helpers/arch_features.mk
+++ b/make_helpers/arch_features.mk
@@ -437,3 +437,6 @@
 
 # Flag to enable access to Guarded Control Stack (FEAT_GCS).
 ENABLE_FEAT_GCS				?=	0
+
+# Flag to enable Fine Grained Write Traps (FEAT_FGWTE3) for EL3.
+ENABLE_FEAT_FGWTE3			?=	0
diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk
index 7f1dfc6..8e16829 100644
--- a/plat/arm/board/fvp/platform.mk
+++ b/plat/arm/board/fvp/platform.mk
@@ -61,6 +61,7 @@
       ENABLE_FEAT_D128		:= 2
       ENABLE_FEAT_FPMR		:= 2
       ENABLE_FEAT_MOPS		:= 2
+      ENABLE_FEAT_FGWTE3	:= 2
 endif
 
 ENABLE_SYS_REG_TRACE_FOR_NS	:= 2