summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
authorLouis Mayencourt <louis.mayencourt@arm.com>2019-07-09 11:40:55 +0100
committerLouis Mayencourt <louis.mayencourt@arm.com>2019-07-17 10:43:48 +0100
commitb8b31ad00046e163c40427758ec8cf976008e99b (patch)
tree85dead6c1119f9bf03a280a8b864d73a80efabcd /common
parent2cbeee4d519bac0d79da98faae969fae9f9558f9 (diff)
backtrace: Strip PAC field when PAUTH is enabled
When pointer authentication is enabled, the LR value saved on the stack contains a Pointer Authentication Code (PAC). It must be stripped to retrieve the return address. 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 Change-Id: I21d804e58200dfeca1da4c2554690bed5d191936 Signed-off-by: Louis Mayencourt <louis.mayencourt@arm.com>
Diffstat (limited to 'common')
-rw-r--r--common/backtrace/backtrace.c59
1 files changed, 59 insertions, 0 deletions
diff --git a/common/backtrace/backtrace.c b/common/backtrace/backtrace.c
index ecc65c92..53f8b071 100644
--- a/common/backtrace/backtrace.c
+++ b/common/backtrace/backtrace.c
@@ -37,6 +37,47 @@ struct frame_record {
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 @@ static bool is_address_readable(uintptr_t addr)
{
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 @@ static void unwind_stack(struct frame_record *fr, uintptr_t current_pc,
*/
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.