feat(trbe): enable access to trace buffer control registers from lower NS EL

Introduced a build flag 'ENABLE_TRBE_FOR_NS' to enable trace buffer
control registers access in NS-EL2, or NS-EL1 (when NS-EL2 is
implemented but unused).

Change-Id: I285a672ccd395eebd377714c992bb21062a729cc
Signed-off-by: Manish V Badarkhe <Manish.Badarkhe@arm.com>
diff --git a/Makefile b/Makefile
index b4bebf1..c8ffdab 100644
--- a/Makefile
+++ b/Makefile
@@ -964,6 +964,7 @@
         ENABLE_FEAT_RNG \
         ENABLE_FEAT_SB \
         PSA_FWU_SUPPORT \
+        ENABLE_TRBE_FOR_NS \
 )))
 
 $(eval $(call assert_numerics,\
@@ -1064,6 +1065,7 @@
         NR_OF_FW_BANKS \
         NR_OF_IMAGES_IN_FW_BANK \
         PSA_FWU_SUPPORT \
+        ENABLE_TRBE_FOR_NS \
 )))
 
 ifeq (${SANITIZE_UB},trap)
diff --git a/bl31/bl31.mk b/bl31/bl31.mk
index 1fdf545..dc3d3c0 100644
--- a/bl31/bl31.mk
+++ b/bl31/bl31.mk
@@ -90,6 +90,10 @@
 BL31_SOURCES		+=	lib/extensions/mpam/mpam.c
 endif
 
+ifeq (${ENABLE_TRBE_FOR_NS},1)
+BL31_SOURCES		+=	lib/extensions/trbe/trbe.c
+endif
+
 ifeq (${WORKAROUND_CVE_2017_5715},1)
 BL31_SOURCES		+=	lib/cpus/aarch64/wa_cve_2017_5715_bpiall.S	\
 				lib/cpus/aarch64/wa_cve_2017_5715_mmu.S
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index 901a72a..57b880a 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -775,6 +775,12 @@
   functions that wait for an arbitrary time length (udelay and mdelay). The
   default value is 0.
 
+- ``ENABLE_TRBE_FOR_NS``: This flag is used to enable access of trace buffer
+  control registers from NS ELs, NS-EL2 or NS-EL1(when NS-EL2 is implemented
+  but unused) when FEAT_TRBE is implemented. TRBE is an optional architectural
+  feature for AArch64. The default is 0 and it is automatically disabled when
+  the target architecture is AArch32.
+
 GICv3 driver options
 --------------------
 
diff --git a/include/arch/aarch64/arch.h b/include/arch/aarch64/arch.h
index d71ec00..47e02ce 100644
--- a/include/arch/aarch64/arch.h
+++ b/include/arch/aarch64/arch.h
@@ -192,6 +192,11 @@
 #define ID_AA64DFR0_PMS_SHIFT	U(32)
 #define ID_AA64DFR0_PMS_MASK	ULL(0xf)
 
+/* ID_AA64DFR0_EL1.TraceBuffer definitions */
+#define ID_AA64DFR0_TRACEBUFFER_SHIFT		U(44)
+#define ID_AA64DFR0_TRACEBUFFER_MASK		ULL(0xf)
+#define ID_AA64DFR0_TRACEBUFFER_SUPPORTED	ULL(1)
+
 /* ID_AA64DFR0_EL1.MTPMU definitions (for ARMv8.6+) */
 #define ID_AA64DFR0_MTPMU_SHIFT		U(48)
 #define ID_AA64DFR0_MTPMU_MASK		ULL(0xf)
diff --git a/include/lib/extensions/trbe.h b/include/lib/extensions/trbe.h
new file mode 100644
index 0000000..1753ab6
--- /dev/null
+++ b/include/lib/extensions/trbe.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef TRBE_H
+#define TRBE_H
+
+void trbe_enable(void);
+
+#endif /* TRBE_H */
diff --git a/lib/el3_runtime/aarch64/context_mgmt.c b/lib/el3_runtime/aarch64/context_mgmt.c
index 1a765e7..9eb66ed 100644
--- a/lib/el3_runtime/aarch64/context_mgmt.c
+++ b/lib/el3_runtime/aarch64/context_mgmt.c
@@ -22,6 +22,7 @@
 #include <lib/extensions/mpam.h>
 #include <lib/extensions/spe.h>
 #include <lib/extensions/sve.h>
+#include <lib/extensions/trbe.h>
 #include <lib/extensions/twed.h>
 #include <lib/utils.h>
 
@@ -348,6 +349,11 @@
 #if ENABLE_MPAM_FOR_LOWER_ELS
 	mpam_enable(el2_unused);
 #endif
+
+#if ENABLE_TRBE_FOR_NS
+	trbe_enable();
+#endif /* ENABLE_TRBE_FOR_NS */
+
 #endif
 }
 
diff --git a/lib/extensions/trbe/trbe.c b/lib/extensions/trbe/trbe.c
new file mode 100644
index 0000000..9f754d5
--- /dev/null
+++ b/lib/extensions/trbe/trbe.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2021, Arm Limited. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch.h>
+#include <arch_helpers.h>
+#include <lib/el3_runtime/pubsub.h>
+#include <lib/extensions/trbe.h>
+
+static void tsb_csync(void)
+{
+	/*
+	 * The assembler does not yet understand the tsb csync mnemonic
+	 * so use the equivalent hint instruction.
+	 */
+	__asm__ volatile("hint #18");
+}
+
+static bool trbe_supported(void)
+{
+	uint64_t features;
+
+	features = read_id_aa64dfr0_el1() >> ID_AA64DFR0_TRACEBUFFER_SHIFT;
+	return ((features & ID_AA64DFR0_TRACEBUFFER_MASK) ==
+		ID_AA64DFR0_TRACEBUFFER_SUPPORTED);
+}
+
+void trbe_enable(void)
+{
+	uint64_t val;
+
+	if (trbe_supported()) {
+		/*
+		 * MDCR_EL3.NSTB = 0b11
+		 * Allow access of trace buffer control registers from NS-EL1
+		 * and NS-EL2, tracing is prohibited in Secure and Realm state
+		 * (if implemented).
+		 */
+		val = read_mdcr_el3();
+		val |= MDCR_NSTB(MDCR_NSTB_EL1);
+		write_mdcr_el3(val);
+	}
+}
+
+static void *trbe_drain_trace_buffers_hook(const void *arg __unused)
+{
+	if (trbe_supported()) {
+		/*
+		 * Before switching from normal world to secure world
+		 * the trace buffers need to be drained out to memory. This is
+		 * required to avoid an invalid memory access when TTBR is switched
+		 * for entry to S-EL1.
+		 */
+		tsb_csync();
+		dsbnsh();
+	}
+
+	return (void *)0;
+}
+
+SUBSCRIBE_TO_EVENT(cm_entering_secure_world, trbe_drain_trace_buffers_hook);
diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk
index 72f84b5..e95d226 100644
--- a/make_helpers/defaults.mk
+++ b/make_helpers/defaults.mk
@@ -355,3 +355,14 @@
 
 # Disable Firmware update support by default
 PSA_FWU_SUPPORT			:= 0
+
+# By default, disable access of trace buffer control registers from NS
+# lower ELs  i.e. NS-EL2, or NS-EL1 if NS-EL2 implemented but unused
+# if FEAT_TRBE is implemented.
+# Note FEAT_TRBE is only supported on AArch64 - therefore do not enable in
+# AArch32.
+ifneq (${ARCH},aarch32)
+    ENABLE_TRBE_FOR_NS		:= 0
+else
+    override ENABLE_TRBE_FOR_NS	:= 0
+endif