Some CHK2 (Check Register Against Bounds) instructions, such as opcode
02FA, cause spurious illegal instruction exceptions, despite being valid
on Motorola MC68020 and later processors and used in existing software.
With this patch, QEMU:
- Translates CHK2 and CMP2 (Compare Register Against Bounds)
instructions [1] having any valid size or effective address. CHK2 and
CMP2 use the same opcodes but differ in bit 11 of the extension word.
(BITREV or BYTEREV instructions for ColdFire family processors [2],
which use similar opcodes, are not captured, however.)
- Implements CMP2, which "is identical to CHK2 except that it sets
condition codes rather than taking an exception" [1].
- Populates the correct "logical address of the instruction following
the instruction that caused the trap" [3] in the exception stack frame
for CHK and CHK2 exceptions, according to the number of words in the
instruction, including extension words, rather than default lengths.
An existing test for CHK2 is enabled, and tests are added for CHK, CHK2,
and CMP2.
References:
[1] Motorola M68000 Family Programmer's Reference Manual (M68000PM/AD),
Rev. 1, 1992, pages 4-72 to 4-73 and 4-82 to 4-83.
[2] Freescale Semiconductor, ColdFire Family Programmer's Reference Manual
(CFPRM), Rev. 3, 2005, pages 4-19 and 4-26.
[3] Motorola M68040 User's Manual (M68040UM/AD), 1993, page 8-8.
Signed-off-by: William Hooper <[email protected]>
---
target/m68k/helper.h | 5 +-
target/m68k/op_helper.c | 16 ++++--
target/m68k/translate.c | 31 ++++++++----
tests/tcg/m68k/trap.c | 105 ++++++++++++++++++++++++++++++++++++++--
4 files changed, 137 insertions(+), 20 deletions(-)
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index 95aa5e53bb..0bda8f8152 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -123,8 +123,9 @@ DEF_HELPER_FLAGS_4(bfclr_mem, TCG_CALL_NO_WG, i32, env,
i32, s32, i32)
DEF_HELPER_FLAGS_4(bfset_mem, TCG_CALL_NO_WG, i32, env, i32, s32, i32)
DEF_HELPER_FLAGS_4(bfffo_mem, TCG_CALL_NO_WG, i64, env, i32, s32, i32)
-DEF_HELPER_3(chk, void, env, s32, s32)
-DEF_HELPER_4(chk2, void, env, s32, s32, s32)
+DEF_HELPER_4(chk, void, env, s32, s32, int)
+DEF_HELPER_5(chk2, void, env, s32, s32, s32, int)
+DEF_HELPER_4(cmp2, void, env, s32, s32, s32)
#if !defined(CONFIG_USER_ONLY)
DEF_HELPER_3(ptest, void, env, i32, i32)
diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c
index e9c20a8e03..1fa1b0b33b 100644
--- a/target/m68k/op_helper.c
+++ b/target/m68k/op_helper.c
@@ -1090,7 +1090,7 @@ uint64_t HELPER(bfffo_mem)(CPUM68KState *env, uint32_t
addr,
return n | ffo;
}
-void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub)
+void HELPER(chk)(CPUM68KState *env, int32_t val, int32_t ub, int ilen)
{
/*
* From the specs:
@@ -1106,11 +1106,12 @@ void HELPER(chk)(CPUM68KState *env, int32_t val,
int32_t ub)
env->cc_c = 0 <= ub ? val < 0 || val > ub : val > ub && val < 0;
if (val < 0 || val > ub) {
- raise_exception_format2(env, EXCP_CHK, 2, GETPC());
+ raise_exception_format2(env, EXCP_CHK, ilen, GETPC());
}
}
-void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub)
+void HELPER(chk2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub,
+ int ilen)
{
/*
* From the specs:
@@ -1127,6 +1128,13 @@ void HELPER(chk2)(CPUM68KState *env, int32_t val,
int32_t lb, int32_t ub)
env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb;
if (env->cc_c) {
- raise_exception_format2(env, EXCP_CHK, 4, GETPC());
+ raise_exception_format2(env, EXCP_CHK, ilen, GETPC());
}
}
+
+void HELPER(cmp2)(CPUM68KState *env, int32_t val, int32_t lb, int32_t ub)
+{
+ /* Identical to CHK2 (above) but doesn't raise an exception */
+ env->cc_z = val != lb && val != ub;
+ env->cc_c = lb <= ub ? val < lb || val > ub : val > ub && val < lb;
+}
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index eb1ba15074..cc289b5105 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -4237,7 +4237,7 @@ DISAS_INSN(ff1)
DISAS_INSN(chk)
{
- TCGv src, reg;
+ TCGv src, reg, ilen;
int opsize;
switch ((insn >> 7) & 3) {
@@ -4258,13 +4258,14 @@ DISAS_INSN(chk)
reg = gen_extend(s, DREG(insn, 9), opsize, 1);
gen_flush_flags(s);
- gen_helper_chk(tcg_env, reg, src);
+ ilen = tcg_constant_i32(s->pc - s->base.pc_next);
+ gen_helper_chk(tcg_env, reg, src, ilen);
}
DISAS_INSN(chk2)
{
uint16_t ext;
- TCGv addr1, addr2, bound1, bound2, reg;
+ TCGv addr1, addr2, bound1, bound2, reg, ilen;
int opsize;
switch ((insn >> 9) & 3) {
@@ -4283,10 +4284,6 @@ DISAS_INSN(chk2)
}
ext = read_im16(env, s);
- if ((ext & 0x0800) == 0) {
- gen_exception(s, s->base.pc_next, EXCP_ILLEGAL);
- return;
- }
addr1 = gen_lea(env, s, insn, OS_UNSIZED);
addr2 = tcg_temp_new();
@@ -4303,7 +4300,12 @@ DISAS_INSN(chk2)
}
gen_flush_flags(s);
- gen_helper_chk2(tcg_env, reg, bound1, bound2);
+ if ((ext & 0x0800) == 0) {
+ gen_helper_cmp2(tcg_env, reg, bound1, bound2);
+ } else {
+ ilen = tcg_constant_i32(s->pc - s->base.pc_next);
+ gen_helper_chk2(tcg_env, reg, bound1, bound2, ilen);
+ }
}
static void m68k_copy_line(TCGv dst, TCGv src, int index)
@@ -5770,8 +5772,11 @@ void register_m68k_insns (CPUM68KState *env)
BASE(undef, 0000, 0000);
INSN(arith_im, 0080, fff8, CF_ISA_A);
INSN(arith_im, 0000, ff00, M68K);
- INSN(chk2, 00c0, f9c0, CHK2);
INSN(bitrev, 00c0, fff8, CF_ISA_APLUSC);
+ INSN(chk2, 00d0, fff8, CHK2);
+ INSN(chk2, 00e8, fff8, CHK2);
+ INSN(chk2, 00f0, fff8, CHK2);
+ INSN(chk2, 00f8, fffc, CHK2);
BASE(bitop_reg, 0100, f1c0);
BASE(bitop_reg, 0140, f1c0);
BASE(bitop_reg, 0180, f1c0);
@@ -5781,12 +5786,20 @@ void register_m68k_insns (CPUM68KState *env)
INSN(arith_im, 0200, ff00, M68K);
INSN(undef, 02c0, ffc0, M68K);
INSN(byterev, 02c0, fff8, CF_ISA_APLUSC);
+ INSN(chk2, 02d0, fff8, CHK2);
+ INSN(chk2, 02e8, fff8, CHK2);
+ INSN(chk2, 02f0, fff8, CHK2);
+ INSN(chk2, 02f8, fffc, CHK2);
INSN(arith_im, 0480, fff8, CF_ISA_A);
INSN(arith_im, 0400, ff00, M68K);
INSN(undef, 04c0, ffc0, M68K);
INSN(arith_im, 0600, ff00, M68K);
INSN(undef, 06c0, ffc0, M68K);
INSN(ff1, 04c0, fff8, CF_ISA_APLUSC);
+ INSN(chk2, 04d0, fff8, CHK2);
+ INSN(chk2, 04e8, fff8, CHK2);
+ INSN(chk2, 04f0, fff8, CHK2);
+ INSN(chk2, 04f8, fffc, CHK2);
INSN(arith_im, 0680, fff8, CF_ISA_A);
INSN(arith_im, 0c00, ff38, CF_ISA_A);
INSN(arith_im, 0c00, ff00, M68K);
diff --git a/tests/tcg/m68k/trap.c b/tests/tcg/m68k/trap.c
index 96cac18d4d..a1378021b1 100644
--- a/tests/tcg/m68k/trap.c
+++ b/tests/tcg/m68k/trap.c
@@ -33,6 +33,7 @@ static void sig_handler(int sig, siginfo_t *si, void *puc)
"move.l #0f, (%[ad])\n\tmove.l #1f, (%[pc])\n" S "\n1:\n"
#define CHECK_SIG do { assert(got_signal); got_signal = 0; } while (0)
+#define OUT_CZ "\n\tscs %0\n\tseq %1" : [c] "=r"(c), [z] "=r"(z) :
int main(int argc, char **argv)
{
@@ -41,6 +42,73 @@ int main(int argc, char **argv)
.sa_flags = SA_SIGINFO
};
int t0, t1;
+ char bbounds[2] = { 0, 2 };
+ short wbounds[2] = { 0, 2 };
+ int lbounds[2] = { 0, 2 };
+ static int sbounds[2] = { 0, 2 };
+ void *intermediate;
+ char c, z;
+
+ /*
+ * Tests for CMP2, which sets the condition code register just as
+ * CHK2 does but doesn't raise out-of-bounds exceptions:
+ */
+ asm volatile("cmp2.b %2, %3" OUT_CZ "m"(bbounds), "d"(-1));
+ assert(c && !z);
+ asm volatile("cmp2.b %2, %3" OUT_CZ "m"(bbounds), "d"(0));
+ assert(!c && z);
+ asm volatile("cmp2.b %2, %3" OUT_CZ "m"(bbounds), "d"(1));
+ assert(!c && !z);
+ asm volatile("cmp2.b %2, %3" OUT_CZ "m"(bbounds), "d"(2));
+ assert(!c && z);
+ asm volatile("cmp2.b %2, %3" OUT_CZ "m"(bbounds), "d"(3));
+ assert(c && !z);
+ asm volatile("cmp2.w %2, %3" OUT_CZ "m"(wbounds), "d"(-1));
+ assert(c && !z);
+ asm volatile("cmp2.w %2, %3" OUT_CZ "m"(wbounds), "d"(0));
+ assert(!c && z);
+ asm volatile("cmp2.w %2, %3" OUT_CZ "m"(wbounds), "d"(1));
+ assert(!c && !z);
+ asm volatile("cmp2.w %2, %3" OUT_CZ "m"(wbounds), "d"(2));
+ assert(!c && z);
+ asm volatile("cmp2.w %2, %3" OUT_CZ "m"(wbounds), "d"(3));
+ assert(c && !z);
+ asm volatile("cmp2.l %2, %3" OUT_CZ "m"(lbounds), "d"(-1));
+ assert(c && !z);
+ asm volatile("cmp2.l %2, %3" OUT_CZ "m"(lbounds), "d"(0));
+ assert(!c && z);
+ asm volatile("cmp2.l %2, %3" OUT_CZ "m"(lbounds), "d"(1));
+ assert(!c && !z);
+ asm volatile("cmp2.l %2, %3" OUT_CZ "m"(lbounds), "d"(2));
+ assert(!c && z);
+ asm volatile("cmp2.l %2, %3" OUT_CZ "m"(lbounds), "d"(3));
+ assert(c && !z);
+
+ /*
+ * CHK2 shouldn't raise out-of-bounds exceptions, either, when the
+ * register value is within bounds:
+ */
+ asm volatile("chk2.b %2, %3" OUT_CZ "m"(bbounds), "d"(0));
+ assert(!c && z);
+ asm volatile("chk2.w %2, %3" OUT_CZ "m"(wbounds), "d"(0));
+ assert(!c && z);
+ asm volatile("chk2.l %2, %3" OUT_CZ "m"(lbounds), "d"(0));
+ assert(!c && z);
+
+ /* Address register indirect addressing (without displacement) */
+ asm volatile("chk2.l %2, %3" OUT_CZ "Q"(lbounds), "d"(2));
+ assert(!c && z);
+
+ /* Absolute long addressing */
+ asm volatile("chk2.l %2, %3" OUT_CZ "m"(sbounds), "d"(2));
+ assert(!c && z);
+
+ /* Memory indirect preindexed addressing */
+ intermediate = (void *)lbounds - 0x0D0D0D0D;
+ asm volatile("chk2.l %2@(0xBDBDBDBD,%3:l:4)@(0x0D0D0D0D), %4" OUT_CZ
+ "a"((void *)&intermediate - 0xBDBDBDBD - 0xEEEE * 4),
+ "r"(0xEEEE), "d"(2));
+ assert(!c && z);
sigaction(SIGILL, &act, NULL);
sigaction(SIGTRAP, &act, NULL);
@@ -51,13 +119,40 @@ int main(int argc, char **argv)
asm volatile(FMT2_STR("0:\tchk %0, %1") : : "d"(0), "d"(-1), FMT_INS);
CHECK_SIG;
-#if 0
- /* FIXME: chk2 not correctly translated. */
- int bounds[2] = { 0, 1 };
+ /*
+ * The extension word here should be counted when computing the
+ * address of the next instruction in the exception stack frame
+ */
+ asm volatile(FMT2_STR("0:\tchk %0, %1")
+ : : "m"(lbounds), "d"(-1), FMT_INS);
+ CHECK_SIG;
+
+ asm volatile(FMT2_STR("0:\tchk2.b %0, %1")
+ : : "m"(bbounds), "d"(3), FMT_INS);
+ CHECK_SIG;
+
+ asm volatile(FMT2_STR("0:\tchk2.w %0, %1")
+ : : "m"(wbounds), "d"(3), FMT_INS);
+ CHECK_SIG;
+
asm volatile(FMT2_STR("0:\tchk2.l %0, %1")
- : : "m"(bounds), "d"(2), FMT_INS);
+ : : "m"(lbounds), "d"(3), FMT_INS);
+ CHECK_SIG;
+
+ /* Absolute long addressing */
+ asm volatile(FMT2_STR("0:\tchk2.l %0, %1")
+ : : "m"(sbounds), "d"(3), FMT_INS);
+ CHECK_SIG;
+
+ /*
+ * Memory indirect preindexed addressing; also, the six extension
+ * words here should be counted when computing the address of the
+ * next instruction in the exception stack frame
+ */
+ asm volatile(FMT2_STR("0:\tchk2.l %0@(0xBDBDBDBD,%1:l:4)@(0x0D0D0D0D), %2")
+ : : "a"((void *)&intermediate - 0xBDBDBDBD - 0xEEEE * 4),
+ "r"(0xEEEE), "d"(3), FMT_INS);
CHECK_SIG;
-#endif
asm volatile(FMT2_STR("cmp.l %0, %1\n0:\ttrapv")
: : "d"(INT_MIN), "d"(1), FMT_INS);