The architecture scrambles only the LR word of the {FP, LR} frame that
allocframe stores.  With the idef parser enabled, fFRAME_SCRAMBLE built
the XOR key with FRAMEKEY in both halves of the 64-bit value, so the
stored FP was scrambled as well.

deallocframe is also parser-generated and applies the same incorrect
transform a second time, which masks the error.  dealloc_return uses the
hand-written TCG implementation that correctly descrambles only LR, so
it leaves FP corrupt when returning from a frame allocated by the parser.

Add a regression test that sets a nonzero FRAMEKEY, allocates a frame,
and returns through dealloc_return to validate the restored FP.

Uncovered by testing some boot images using QuRT.

Reviewed-by: Marco Liebel <[email protected]>
Signed-off-by: Brian Cain <[email protected]>
---
 target/hexagon/idef-parser/parser-helpers.c |  2 +-
 tests/tcg/hexagon/framekey.c                | 54 +++++++++++++++++++++
 tests/tcg/hexagon/meson.build               |  1 +
 3 files changed, 56 insertions(+), 1 deletion(-)
 create mode 100644 tests/tcg/hexagon/framekey.c

diff --git a/target/hexagon/idef-parser/parser-helpers.c 
b/target/hexagon/idef-parser/parser-helpers.c
index 49cc11fddd8..14840d98737 100644
--- a/target/hexagon/idef-parser/parser-helpers.c
+++ b/target/hexagon/idef-parser/parser-helpers.c
@@ -1989,7 +1989,7 @@ HexValue gen_rvalue_fscr(Context *c, YYLTYPE *locp, 
HexValue *value)
     *value = gen_rvalue_extend(c, locp, value);
     OUT(c, locp, "gen_read_reg(", &frame_key, ", HEX_REG_FRAMEKEY);\n");
     OUT(c, locp, "tcg_gen_concat_i32_i64(",
-        &key, ", ", &frame_key, ", ", &frame_key, ");\n");
+        &key, ", tcg_constant_tl(0), ", &frame_key, ");\n");
     OUT(c, locp, "tcg_gen_xor_i64(", &res, ", ", value, ", ", &key, ");\n");
     return res;
 }
diff --git a/tests/tcg/hexagon/framekey.c b/tests/tcg/hexagon/framekey.c
new file mode 100644
index 00000000000..14c7fcbc654
--- /dev/null
+++ b/tests/tcg/hexagon/framekey.c
@@ -0,0 +1,54 @@
+/*
+ *  Copyright(c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ *
+ *  SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+
+int err;
+
+#include "hex_test.h"
+
+/*
+ * allocframe pushes {LR, FP}, scrambling LR with FRAMEKEY.  Return through
+ * dealloc_return and report the FP that it restores.
+ */
+static inline uint32_t frame_roundtrip(uint32_t key, uint32_t fp)
+{
+    uint32_t fp_back, saved_fp, saved_lr;
+
+    asm volatile("%[saved_fp] = r30\n\t"
+                 "%[saved_lr] = r31\n\t"
+                 "framekey = %[key]\n\t"
+                 "r30 = %[fp]\n\t"
+                 "r31 = ##1f\n\t"
+                 "allocframe(#0)\n\t"
+                 "r31 = #0\n\t"
+                 "dealloc_return\n\t"
+                 "1:\n\t"
+                 "%[fp_back] = r30\n\t"
+                 "framekey = %[zero]\n\t"
+                 "r30 = %[saved_fp]\n\t"
+                 "r31 = %[saved_lr]\n"
+                 : [fp_back] "=&r"(fp_back),
+                   [saved_fp] "=&r"(saved_fp), [saved_lr] "=&r"(saved_lr)
+                 : [key] "r"(key), [fp] "r"(fp), [zero] "r"(0)
+                 : "memory", "framekey");
+    return fp_back;
+}
+
+static void check_frame(uint32_t key, uint32_t fp)
+{
+    check32(frame_roundtrip(key, fp), fp);
+}
+
+int main()
+{
+    check_frame(0, 0x1234fff0);
+    check_frame(0xf21f20a0, 0x1234fff0);
+
+    puts(err ? "FAIL" : "PASS");
+    return err;
+}
diff --git a/tests/tcg/hexagon/meson.build b/tests/tcg/hexagon/meson.build
index a6ace4c77c2..0fd62ce7e4c 100644
--- a/tests/tcg/hexagon/meson.build
+++ b/tests/tcg/hexagon/meson.build
@@ -68,6 +68,7 @@ tests += {
   'overflow.c': {'cflags': cflags, 'exe_name': 'hex-overflow'},
   'preg_alias.c': {'cflags': cflags},
   'dual_stores.c': {'cflags': cflags},
+  'framekey.c': {'cflags': cflags},
   'multi_result.c': {'cflags': cflags},
   'mem_noshuf.c': {'cflags': cflags},
   'mem_noshuf_exception.c': {'cflags': cflags},
-- 
2.34.1

Reply via email to