target-arm: Fix handling of STM (user) with r15 in register list
The A32 encoding of LDM distinguishes LDM (user) from LDM (exception return) based on whether r15 is in the register list. However for STM (user) there is no equivalent distinction. We were incorrectly treating "r15 in list" as indicating exception return for both LDM and STM, with the result that an STM (user) involving r15 went into an infinite loop. Fix this; note that the value stored for r15 in this case is the current PC regardless of our current mode. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Message-id: 1426015125-5521-1-git-send-email-peter.maydell@linaro.org
This commit is contained in:
parent
f0bb55890a
commit
da3e53ddcb
@ -8859,17 +8859,23 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
|
|||||||
case 0x08:
|
case 0x08:
|
||||||
case 0x09:
|
case 0x09:
|
||||||
{
|
{
|
||||||
int j, n, user, loaded_base;
|
int j, n, loaded_base;
|
||||||
|
bool exc_return = false;
|
||||||
|
bool is_load = extract32(insn, 20, 1);
|
||||||
|
bool user = false;
|
||||||
TCGv_i32 loaded_var;
|
TCGv_i32 loaded_var;
|
||||||
/* load/store multiple words */
|
/* load/store multiple words */
|
||||||
/* XXX: store correct base if write back */
|
/* XXX: store correct base if write back */
|
||||||
user = 0;
|
|
||||||
if (insn & (1 << 22)) {
|
if (insn & (1 << 22)) {
|
||||||
|
/* LDM (user), LDM (exception return) and STM (user) */
|
||||||
if (IS_USER(s))
|
if (IS_USER(s))
|
||||||
goto illegal_op; /* only usable in supervisor mode */
|
goto illegal_op; /* only usable in supervisor mode */
|
||||||
|
|
||||||
if ((insn & (1 << 15)) == 0)
|
if (is_load && extract32(insn, 15, 1)) {
|
||||||
user = 1;
|
exc_return = true;
|
||||||
|
} else {
|
||||||
|
user = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
rn = (insn >> 16) & 0xf;
|
rn = (insn >> 16) & 0xf;
|
||||||
addr = load_reg(s, rn);
|
addr = load_reg(s, rn);
|
||||||
@ -8903,7 +8909,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
|
|||||||
j = 0;
|
j = 0;
|
||||||
for(i=0;i<16;i++) {
|
for(i=0;i<16;i++) {
|
||||||
if (insn & (1 << i)) {
|
if (insn & (1 << i)) {
|
||||||
if (insn & (1 << 20)) {
|
if (is_load) {
|
||||||
/* load */
|
/* load */
|
||||||
tmp = tcg_temp_new_i32();
|
tmp = tcg_temp_new_i32();
|
||||||
gen_aa32_ld32u(tmp, addr, get_mem_index(s));
|
gen_aa32_ld32u(tmp, addr, get_mem_index(s));
|
||||||
@ -8968,7 +8974,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
|
|||||||
if (loaded_base) {
|
if (loaded_base) {
|
||||||
store_reg(s, rn, loaded_var);
|
store_reg(s, rn, loaded_var);
|
||||||
}
|
}
|
||||||
if ((insn & (1 << 22)) && !user) {
|
if (exc_return) {
|
||||||
/* Restore CPSR from SPSR. */
|
/* Restore CPSR from SPSR. */
|
||||||
tmp = load_cpu_field(spsr);
|
tmp = load_cpu_field(spsr);
|
||||||
gen_set_cpsr(tmp, CPSR_ERET_MASK);
|
gen_set_cpsr(tmp, CPSR_ERET_MASK);
|
||||||
|
Loading…
Reference in New Issue
Block a user