[x86] Fix CPSR store bug in LR register

There's a race condition on CPSR store (only if mode is changed) where,
if an IRQ is pending, the IRQ will be served, but the saved LR value
will be wrong (will skip the return instruction).
Fixed this and improved the logic a bit to make it faster and not use
unnecessary save slots.
This commit is contained in:
David Guillen Fandos 2021-12-10 18:32:04 +01:00
parent 1667597a43
commit 61ef776fed
2 changed files with 19 additions and 19 deletions

View File

@ -1365,24 +1365,19 @@ u32 function_cc execute_spsr_restore(u32 address)
execute_read_##psr_reg(rv); \ execute_read_##psr_reg(rv); \
generate_store_reg(rv, rd) \ generate_store_reg(rv, rd) \
// store_mask and address are stored in the SAVE slots, since there's no real // Does mode-change magic (including IRQ checks)
// register space to nicely pass them.
u32 execute_store_cpsr_body() u32 execute_store_cpsr_body()
{ {
if(reg[REG_SAVE] & 0xFF)
{
set_cpu_mode(cpu_modes[reg[REG_CPSR] & 0x1F]); set_cpu_mode(cpu_modes[reg[REG_CPSR] & 0x1F]);
if((io_registers[REG_IE] & io_registers[REG_IF]) && if((io_registers[REG_IE] & io_registers[REG_IF]) &&
io_registers[REG_IME] && ((reg[REG_CPSR] & 0x80) == 0)) io_registers[REG_IME] && ((reg[REG_CPSR] & 0x80) == 0))
{ {
reg_mode[MODE_IRQ][6] = reg[REG_SAVE2] + 4; reg_mode[MODE_IRQ][6] = reg[REG_PC] + 4;
spsr[MODE_IRQ] = reg[REG_CPSR]; spsr[MODE_IRQ] = reg[REG_CPSR];
reg[REG_CPSR] = (reg[REG_CPSR] & 0xFFFFFF00) | 0xD2; reg[REG_CPSR] = (reg[REG_CPSR] & 0xFFFFFF00) | 0xD2;
set_cpu_mode(MODE_IRQ); set_cpu_mode(MODE_IRQ);
return 0x00000018; return 0x00000018;
} }
}
return 0; return 0;
} }
@ -1397,7 +1392,7 @@ u32 execute_store_cpsr_body()
#define execute_store_cpsr() \ #define execute_store_cpsr() \
generate_load_imm(a1, psr_masks[psr_field]); \ generate_load_imm(a1, psr_masks[psr_field]); \
generate_store_reg_i32(pc + 4, REG_SAVE2); \ generate_store_reg_i32(pc, REG_PC); \
generate_function_call(execute_store_cpsr) \ generate_function_call(execute_store_cpsr) \
/* spsr[reg[CPU_MODE]] = (new_spsr & store_mask) | (old_spsr & (~store_mask))*/ /* spsr[reg[CPU_MODE]] = (new_spsr & store_mask) | (old_spsr & (~store_mask))*/

View File

@ -448,7 +448,7 @@ load_stubs( s8, movsbl, ~0, 0, read_memory8s)
# arg0 (%eax) = new_cpsr # arg0 (%eax) = new_cpsr
# arg1 (%edx) = store_mask # arg1 (%edx) = store_mask
defsymbl(execute_store_cpsr) defsymbl(execute_store_cpsr)
mov %edx, REG_SAVE(REG_BASE) # save store_mask mov %edx, %esi # save store_mask for later
mov %eax, %ecx # ecx = new_cpsr mov %eax, %ecx # ecx = new_cpsr
and %edx, %ecx # ecx = new_cpsr & store_mask and %edx, %ecx # ecx = new_cpsr & store_mask
@ -458,13 +458,18 @@ defsymbl(execute_store_cpsr)
or %ecx, %eax # eax = new cpsr combined with old or %ecx, %eax # eax = new cpsr combined with old
mov %eax, REG_CPSR(REG_BASE) # save new cpsr to register mov %eax, REG_CPSR(REG_BASE) # save new cpsr to register
# Check whether any side effects (IRQ) could have happened
test $0xff, %esi
jnz 1f
extract_flags # pull out flag vars from new CPSR
ret
1:
CALL_FUNC(execute_store_cpsr_body) # do the dirty work in this C function CALL_FUNC(execute_store_cpsr_body) # do the dirty work in this C function
extract_flags # pull out flag vars from new CPSR extract_flags # pull out flag vars from new CPSR
cmp $0, %eax # see if return value is 0 cmp $0, %eax # see if return value is 0
jnz 1f # might have changed the PC jnz 2f # might have changed the PC
ret # return ret # return
1: # PC has changed, due to IRQ triggered 2: # PC has changed, due to IRQ triggered
mov %eax, CARG1_REG # Returned addr from C function mov %eax, CARG1_REG # Returned addr from C function
CALL_FUNC(block_lookup_address_arm) # lookup new PC CALL_FUNC(block_lookup_address_arm) # lookup new PC
add $ADDR_SIZE_BYTES, STACK_REG # get rid of current return address add $ADDR_SIZE_BYTES, STACK_REG # get rid of current return address