diff --git a/src/kernel/exch.c b/src/kernel/exch.c index b065f94..72c8cd0 100644 --- a/src/kernel/exch.c +++ b/src/kernel/exch.c @@ -50,6 +50,7 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code) if(code == 0x1020) name = "DMA address error"; if(code == 0x1040) name = "Add-in too large"; if(code == 0x1060) name = "Memory init failed"; + if(code == 0x1080) name = "Stack overflow"; if(name[0]) dtext(1, 9, name); else dprint(1, 9, "%03x", code); @@ -82,6 +83,7 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code) if(code == 0x1020) name = "DMA address error"; if(code == 0x1040) name = "Add-in not fully mapped (too large)"; if(code == 0x1060) name = "Memory initialization failed (heap)"; + if(code == 0x1080) name = "Stack overflow during world switch"; dprint(6, 25, "%03x %s", code, name); diff --git a/src/kernel/kernel.c b/src/kernel/kernel.c index d3abdfe..8aca9b1 100644 --- a/src/kernel/kernel.c +++ b/src/kernel/kernel.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -28,6 +29,9 @@ gint_world_t gint_world_addin = NULL; /* Dynamic flags for all drivers */ uint8_t *gint_driver_flags = NULL; +/* Top of the stack */ +void *gint_stack_top = NULL; + //--- // Initialization and unloading //--- @@ -45,9 +49,12 @@ void kinit(void) expanded region. */ uint32_t uram_end = (uint32_t)mmu_uram() + mmu_uram_size(); - /* Stack space on SH4. 12 kB is a lot, but BFile is known to overflow - from the previous 8 kB design */ - if(isSH4()) uram_end -= 0x3000; + /* Stack space on SH4. 14 kB is a lot, but Fugue's BFile has been seen + overflowing both 8 kB and 12 kB */ + if(isSH4()) { + uram_end -= (gint[HWFS] == HWFS_FUGUE) ? 0x3800 : 0x2000; + gint_stack_top = (void *)uram_end; + } /* VBR is advanced 0x100 bytes because of an unused gap */ uram_end -= (isSH3() ? 0x600 : 0x1100); @@ -61,6 +68,7 @@ void kinit(void) leave 16 kB. */ VBR = (uint32_t)mmu_uram(); uint32_t uram_end = (uint32_t)mmu_uram() + mmu_uram_size() - 0x4000; + gint_stack_top = (void *)uram_end; #endif /* Event handler entry points */ @@ -93,10 +101,7 @@ void kinit(void) gint_driver_flags = malloc(gint_driver_count()); if(!gint_world_os || !gint_world_addin || !gint_driver_flags) - { - extern void gint_panic(uint32_t code); gint_panic(0x1060); - } /* Initialize drivers */ for(int i = 0; i < gint_driver_count(); i++) diff --git a/src/kernel/world.c b/src/kernel/world.c index 1f71403..0cd779d 100644 --- a/src/kernel/world.c +++ b/src/kernel/world.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -126,8 +127,24 @@ void gint_world_switch_out(gint_world_t world_addin, gint_world_t world_os) int gint_world_switch(gint_call_t call) { + extern void *gint_stack_top; gint_world_switch_out(gint_world_addin, gint_world_os); + + /* Watch out for stack overflows */ + uint32_t *canary = gint_stack_top; + if(canary) + *canary = 0xb7c0ffee; + int rc = call.function ? gint_call(call) : 0; + + /* The canary check needs to occur before switching in the gint world; + otherwise we just crash due to the overflow. gint_panic() isn't + really designed to work from the OS world, but it does fine on the + fx-9860G series and sometimes also on the fx-CG series; better crash + attempting to show a panic message than just crash */ + if(canary && *canary != 0xb7c0ffee) + gint_panic(0x1080); + gint_world_switch_in(gint_world_os, gint_world_addin); return rc; }