Emit BIOS SWI entrypoint to ROM arena

This fixes a race condition that happens whenever the ROM cache is flushed but
the RAM one is not, causing any SWI calls (implemented as direct branches) to
jump to random instructions.
The fix could be to flush both caches at the same time (~expensive on
low mem platforms), use indirect jumps (a bit expensive) or emit the SWI
handler below the watermark to ensure it is never flushed. This is cheap
and effective, requires minimal changes.
This commit is contained in:
David Guillen Fandos 2021-09-10 00:30:55 +02:00
parent b431a8a4b6
commit 33f1e25099
6 changed files with 30 additions and 7 deletions

View File

@ -1937,6 +1937,9 @@ extern u32 ldst_lookup_tables[9][16];
void init_emitter(void) {
memcpy(ldst_lookup_tables, ldst_handler_functions, sizeof(ldst_lookup_tables));
rom_cache_watermark = 0;
init_bios_hooks();
}
u32 execute_arm_translate_internal(u32 cycles, void *regptr);

1
cpu.h
View File

@ -163,6 +163,7 @@ void flush_translation_cache_ram(void);
void dump_translation_cache(void);
void init_caches(void);
void init_emitter(void);
void init_bios_hooks(void);
extern u32 reg_mode[7][7];
extern u32 spsr[6];

View File

@ -60,6 +60,7 @@ u32 ewram_code_min = ~0U;
u32 ewram_code_max = 0U;
u32 rom_cache_watermark = 0;
u8 *bios_swi_entrypoint = NULL;
u32 *rom_branch_hash[ROM_BRANCH_HASH_SIZE];
typedef struct
@ -2779,7 +2780,10 @@ block_lookup_address_builder(dual);
} \
#define arm_link_block() \
translation_target = block_lookup_address_arm(branch_target) \
if(branch_target == 0x00000008) \
translation_target = bios_swi_entrypoint; \
else \
translation_target = block_lookup_address_arm(branch_target); \
#define arm_instruction_width 4
@ -2851,10 +2855,11 @@ block_lookup_address_builder(dual);
#define thumb_set_condition(_condition) \
#define thumb_link_block() \
if(branch_target != 0x00000008) \
translation_target = block_lookup_address_thumb(branch_target); \
/* Speed hack to make SWI calls direct jumps */ \
if(branch_target == 0x00000008) \
translation_target = bios_swi_entrypoint; \
else \
translation_target = block_lookup_address_arm(branch_target) \
translation_target = block_lookup_address_thumb(branch_target); \
#define thumb_instruction_width 2
@ -2959,8 +2964,8 @@ block_exit_type block_exits[MAX_EXITS];
no_direct_branch:; \
} \
\
/* SWI branches to the BIOS, this will likely change when \
some HLE BIOS is implemented. */ \
/* SWI branches to the BIOS, unless it's an HLE call, then it is \
not parsed as an exit_point but rather an "instruction" of sorts. */ \
if(type##_opcode_swi) \
{ \
block_exits[block_exit_position].branch_target = 0x00000008; \
@ -3404,6 +3409,15 @@ s32 translate_block_thumb(u32 pc, translation_region_type
return 0;
}
void init_bios_hooks(void)
{
// Pre-generate this entry point so that we can safely invoke fast
// SWI calls from ROM and RAM regardless of cache flushes.
rom_translation_ptr = &rom_translation_cache[rom_cache_watermark];
bios_swi_entrypoint = block_lookup_address_arm(0x8);
rom_cache_watermark = (u32)(rom_translation_ptr - rom_translation_cache);
}
void flush_translation_cache_ram(void)
{
flush_ram_count++;

2
main.c
View File

@ -252,8 +252,8 @@ u32 update_gba(void)
void reset_gba(void)
{
init_main();
init_memory();
init_main();
init_cpu();
reset_sound();
}

View File

@ -3292,6 +3292,8 @@ void init_emitter() {
// Ensure rom flushes do not wipe this area
rom_cache_watermark = (u32)(translation_ptr - rom_translation_cache);
init_bios_hooks();
}
u32 execute_arm_translate_internal(u32 cycles, void *regptr);

View File

@ -2274,6 +2274,9 @@ extern u32 x86_table_info[3][16];
void init_emitter(void) {
memcpy(x86_table_info, x86_table_data, sizeof(x86_table_data));
rom_cache_watermark = 0;
init_bios_hooks();
}
u32 function_cc execute_arm_translate_internal(u32 cycles, void *regptr);