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