Merge "backtrace: Strip PAC field when PAUTH is enabled" into integration
diff --git a/common/backtrace/backtrace.c b/common/backtrace/backtrace.c
index ecc65c9..53f8b07 100644
--- a/common/backtrace/backtrace.c
+++ b/common/backtrace/backtrace.c
@@ -37,6 +37,47 @@
 	uintptr_t return_addr;
 };
 
+/*
+ * Strip the Pointer Authentication Code (PAC) from the address to retrieve the
+ * original one.
+ *
+ * The PAC field is stored on the high bits of the address and defined as:
+ * - PAC field = Xn[54:bottom_PAC_bit], when address tagging is used.
+ * - PAC field = Xn[63:56, 54:bottom_PAC_bit], without address tagging.
+ *
+ * With bottom_PAC_bit = 64 - TCR_ELx.TnSZ
+ */
+#if ENABLE_PAUTH
+static uintptr_t demangle_address(uintptr_t addr)
+{
+	unsigned int el, t0sz, bottom_pac_bit;
+	uint64_t tcr, pac_mask;
+
+	/*
+	 * Different virtual address space size can be defined for each EL.
+	 * Ensure that we use the proper one by reading the corresponding
+	 * TCR_ELx register.
+	 */
+	el = get_current_el();
+
+	if (el == 3U) {
+		tcr = read_tcr_el3();
+	} else if (el == 2U) {
+		tcr = read_tcr_el2();
+	} else {
+		tcr = read_tcr_el1();
+	}
+
+	/* T0SZ = TCR_ELx[5:0] */
+	t0sz = tcr & 0x1f;
+	bottom_pac_bit = 64 - t0sz;
+	pac_mask = (1ULL << bottom_pac_bit) - 1;
+
+	/* demangle the address with the computed mask */
+	return (addr & pac_mask);
+}
+#endif /* ENABLE_PAUTH */
+
 static const char *get_el_str(unsigned int el)
 {
 	if (el == 3U) {
@@ -57,6 +98,15 @@
 {
 	unsigned int el = get_current_el();
 
+#if ENABLE_PAUTH
+	/*
+	 * When pointer authentication is enabled, the LR value saved on the
+	 * stack contains a PAC. It must be stripped to retrieve the return
+	 * address.
+	 */
+	addr = demangle_address(addr);
+#endif
+
 	if (el == 3U) {
 		ats1e3r(addr);
 	} else if (el == 2U) {
@@ -201,6 +251,15 @@
 		 */
 		call_site = fr->return_addr - 4U;
 
+#if ENABLE_PAUTH
+		/*
+		 * When pointer authentication is enabled, the LR value saved on
+		 * the stack contains a PAC. It must be stripped to retrieve the
+		 * return address.
+		 */
+		call_site = demangle_address(call_site);
+#endif
+
 		/*
 		 * If the address is invalid it means that the frame record is
 		 * probably corrupted.