aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2023-09-27 11:18:53 +0100
committerPeter Maydell <peter.maydell@linaro.org>2023-10-19 14:32:13 +0100
commita530e470ea4f70d1207ef62273e43a7d178f53ac (patch)
tree21ff2092b1de71c7460948d7166c30de23804d22
parent40d45b85e045501a3d3d3301f8554ff30adef3ee (diff)
downloadqemu-a530e470ea4f70d1207ef62273e43a7d178f53ac.zip
qemu-a530e470ea4f70d1207ef62273e43a7d178f53ac.tar.gz
qemu-a530e470ea4f70d1207ef62273e43a7d178f53ac.tar.bz2
target/arm: Permit T32 LDM with single register
For the Thumb T32 encoding of LDM, if only a single register is specified in the register list this instruction is UNPREDICTABLE, with the following choices: * instruction UNDEFs * instruction is a NOP * instruction loads a single register * instruction loads an unspecified set of registers Currently we choose to UNDEF (a behaviour chosen in commit 4b222545dbf30 in 2019; previously we treated it as "load the specified single register"). Unfortunately there is real world code out there (which shipped in at least Android 11, 12 and 13) which incorrectly uses this UNPREDICTABLE insn on the assumption that it does a single register load, which is (presumably) what it happens to do on real hardware, and is also what it does on the equivalent A32 encoding. Revert to the pre-4b222545dbf30 behaviour of not UNDEFing for this T32 encoding. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1799 Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20230927101853.39288-1-peter.maydell@linaro.org
-rw-r--r--target/arm/tcg/translate.c37
1 files changed, 23 insertions, 14 deletions
diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c
index 48927fb..b366017 100644
--- a/target/arm/tcg/translate.c
+++ b/target/arm/tcg/translate.c
@@ -7882,7 +7882,7 @@ static void op_addr_block_post(DisasContext *s, arg_ldst_block *a,
}
}
-static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n)
+static bool op_stm(DisasContext *s, arg_ldst_block *a)
{
int i, j, n, list, mem_idx;
bool user = a->u;
@@ -7899,7 +7899,14 @@ static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n)
list = a->list;
n = ctpop16(list);
- if (n < min_n || a->rn == 15) {
+ /*
+ * This is UNPREDICTABLE for n < 1 in all encodings, and we choose
+ * to UNDEF. In the T32 STM encoding n == 1 is also UNPREDICTABLE,
+ * but hardware treats it like the A32 version and implements the
+ * single-register-store, and some in-the-wild (buggy) software
+ * assumes that, so we don't UNDEF on that case.
+ */
+ if (n < 1 || a->rn == 15) {
unallocated_encoding(s);
return true;
}
@@ -7935,8 +7942,7 @@ static bool op_stm(DisasContext *s, arg_ldst_block *a, int min_n)
static bool trans_STM(DisasContext *s, arg_ldst_block *a)
{
- /* BitCount(list) < 1 is UNPREDICTABLE */
- return op_stm(s, a, 1);
+ return op_stm(s, a);
}
static bool trans_STM_t32(DisasContext *s, arg_ldst_block *a)
@@ -7946,11 +7952,10 @@ static bool trans_STM_t32(DisasContext *s, arg_ldst_block *a)
unallocated_encoding(s);
return true;
}
- /* BitCount(list) < 2 is UNPREDICTABLE */
- return op_stm(s, a, 2);
+ return op_stm(s, a);
}
-static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n)
+static bool do_ldm(DisasContext *s, arg_ldst_block *a)
{
int i, j, n, list, mem_idx;
bool loaded_base;
@@ -7979,7 +7984,14 @@ static bool do_ldm(DisasContext *s, arg_ldst_block *a, int min_n)
list = a->list;
n = ctpop16(list);
- if (n < min_n || a->rn == 15) {
+ /*
+ * This is UNPREDICTABLE for n < 1 in all encodings, and we choose
+ * to UNDEF. In the T32 LDM encoding n == 1 is also UNPREDICTABLE,
+ * but hardware treats it like the A32 version and implements the
+ * single-register-load, and some in-the-wild (buggy) software
+ * assumes that, so we don't UNDEF on that case.
+ */
+ if (n < 1 || a->rn == 15) {
unallocated_encoding(s);
return true;
}
@@ -8045,8 +8057,7 @@ static bool trans_LDM_a32(DisasContext *s, arg_ldst_block *a)
unallocated_encoding(s);
return true;
}
- /* BitCount(list) < 1 is UNPREDICTABLE */
- return do_ldm(s, a, 1);
+ return do_ldm(s, a);
}
static bool trans_LDM_t32(DisasContext *s, arg_ldst_block *a)
@@ -8056,16 +8067,14 @@ static bool trans_LDM_t32(DisasContext *s, arg_ldst_block *a)
unallocated_encoding(s);
return true;
}
- /* BitCount(list) < 2 is UNPREDICTABLE */
- return do_ldm(s, a, 2);
+ return do_ldm(s, a);
}
static bool trans_LDM_t16(DisasContext *s, arg_ldst_block *a)
{
/* Writeback is conditional on the base register not being loaded. */
a->w = !(a->list & (1 << a->rn));
- /* BitCount(list) < 1 is UNPREDICTABLE */
- return do_ldm(s, a, 1);
+ return do_ldm(s, a);
}
static bool trans_CLRM(DisasContext *s, arg_CLRM *a)