From 5630814897bb30856f444d1254e56fd7aa26cfdd Mon Sep 17 00:00:00 2001 From: Lephe Date: Fri, 13 Sep 2019 08:10:30 +0200 Subject: [PATCH] core: allow custom panics and exception catching This change introduces two new mechanismes for executing user code when an exception occurs. * This first is the custom panic message, which usually displays "System ERROR". The function that performs this task can now be user-defined. It is also run in user mode because the exception handler rte's into it, allowing it to execute any kind of interrupt-inducing task. The behavior is undefined if this function raises an exception. * The second is an exception-catching function, which (when set) is called every time an exception occurs, and is granted the chance of handling the exception to continue execution normally. It can be used in various ways, the most primitive of which is recording the exception and going back. It runs in interrupt mode and must not raise any kind of exception. --- TODO | 3 +-- include/gint/exc.h | 59 +++++++++++++++++++++++++++++++++++++------ src/core/exch.c | 63 ++++++++++++++++++++++++++++++++++++++-------- src/core/exch.s | 53 ++++++++++++++++++++++++++++++++++++++ src/core/start.c | 7 +----- src/dma/dma.c | 28 --------------------- src/dma/inth.s | 21 +++++++++------- 7 files changed, 171 insertions(+), 63 deletions(-) create mode 100644 src/core/exch.s diff --git a/TODO b/TODO index 81d3b91..34bcdd2 100644 --- a/TODO +++ b/TODO @@ -10,8 +10,7 @@ Tests to run. * topti: all charsets Complementary elements on existing code. -* core: make it possiblé to catch exceptions (gintctl's memory browser) -* core: check if -DX still works in GCC 9 and if not, put spaces +* topti: support unicode fonts * gray: find good values for more models than the Graph 35+E II * render: get rid of GINT_NEED_VRAM and #define vram gint_vram if you need * dma: dma_memcpy() and dma_memset(), possibly requiring alignment diff --git a/include/gint/exc.h b/include/gint/exc.h index 372755a..bbe8436 100644 --- a/include/gint/exc.h +++ b/include/gint/exc.h @@ -9,13 +9,58 @@ #ifndef GINT_EXC #define GINT_EXC -/* gint_exc(): Exception handler fatal error message - Displays a full-screen fatal error message from an exception event code. - Some custom event codes are also used for library failure. This function is - typically called by exception handlers and returns after a dupdate_noint(). - You probably want to soft-lock or quit the add-in after calling it. */ -void gint_exc(uint32_t code); +#include +#include -/* TODO: Add functions to disable fatal errors for some exceptions */ +/* gint_panic(): Panic handler function + This function is called when an uncaught CPU exception is generated. By + default, it displays a full-screen error message with the event code and + basic debugging information. Some custom event codes may be used for kernel + failure scenarios. This function never returns. */ +GNORETURN void gint_panic(uint32_t code); + +/* gint_panic_set(): Change the panic handler function + Sets up a different panic function instead of the default. It the argument + is NULL, restores the default. */ +void gint_panic_set(GNORETURN void (*panic)(uint32_t code)); + +/* gint_exc_catch(): Set a function to catch exceptions + + Sets up an exception-catching function. If an exception occurs, before a + panic is raised, the exception-catching function is executed in interrupt + mode and is given a chance to handle the exception. Passing NULL disables + this feature. + + The exception-catching function can do anything that does not use interrupts + or causes an exception, such as logging the exception or any other useful + mechanism. + + What happens next depends on the return value: + * If it returns 0, the exception is considered handled and execution + continues normally at or after the offending instruction. + * If it returns nonzero, a panic is raised. + + Please be aware that many exceptions are of re-execution type. When + execution restarts after such an exception is handled, the offending + instruction if re-executed. This can cause the exception handling mechanism + to loop. Use gint_exc_skip() to skip over the offending instruction when + needed. Whether an exception is of re-execution type depends on the + exception code. */ +void gint_exc_catch(int (*handler)(uint32_t code)); + +/* gint_exc_skip(): Skip pending exception instructions + + Many exceptions re-execute the offending instruction after the exception is + handled. For instance the TLB miss handler is supposed to load the required + page into memory, so that the instruction that accessed unmapped memory can + be successfully re-executed. + + When an exception-catching function records an exception without solving it, + this re-execution will fail again and the exception handling process will + loop. In such a situation, gint_exc_skip() can be used to manually skip the + offending instruction. + + @instructions Number of instructions to skip (usually only one) */ +void gint_exc_skip(int instructions); #endif /* GINT_EXC */ diff --git a/src/core/exch.c b/src/core/exch.c index 355f943..3fd2a68 100644 --- a/src/core/exch.c +++ b/src/core/exch.c @@ -1,13 +1,15 @@ #define GINT_NEED_VRAM -#include #include +#include +#include +#include #include #define dprint(x, y, ...) dprint(x, y, C_BLACK, C_NONE, __VA_ARGS__) #define dtext(x, y, str) dtext (x, y, str, C_BLACK, C_NONE) -/* gint_exc(): Exception handler fatal error message */ -void gint_exc(GUNUSED uint32_t code) +/* gint_panic_default(): Default panic handler */ +GNORETURN static void gint_default_panic(GUNUSED uint32_t code) { uint32_t TEA = *(volatile uint32_t *)0xff00000c; uint32_t TRA = *(volatile uint32_t *)0xff000020 >> 2; @@ -82,17 +84,56 @@ void gint_exc(GUNUSED uint32_t code) dtext(6, 95, "An unrecoverable error ocurred in the add-in."); dtext(6, 108, "Please press the RESET button to restart the"); dtext(6, 121, "calculator."); + + /* DMA address error handler */ + if(code == 0x1020) + { + #define DMA SH7305_DMA + dprint(6, 141, "SAR0: %08x DAR0: %08x TCR0: %08x", + DMA.DMA0.SAR, DMA.DMA0.DAR, DMA.DMA0.TCR); + dprint(6, 154, "CHCR0: %08x", DMA.DMA0.CHCR); + dprint(6, 167, "SAR1: %08x DAR1: %08x TCR1: %08x", + DMA.DMA1.SAR, DMA.DMA1.DAR, DMA.DMA1.TCR); + dprint(6, 180, "CHCR1: %08x", DMA.DMA1.CHCR); + dprint(6, 193, "DMAOR: %04x", DMA.OR); + #undef DMA + } #endif - dupdate_noint(); + dupdate(); + while(1) sleep(); } -/* gint_exch_tlbh(): Exception and TLB miss handler */ -__attribute__((interrupt_handler)) GSECTION(".gint.exch_tlbh") GALIGNED(4) -void gint_exch_tlbh(void) +/* Panic handler */ +GDATA GNORETURN void (*gint_exc_panic)(uint32_t code) = gint_default_panic; +/* Exception catcher */ +GDATA int (*gint_exc_catcher)(uint32_t code) = NULL; + +/* gint_panic(): Panic handler function */ +void gint_panic(uint32_t code) { - uint32_t EXPEVT = *(volatile uint32_t *)0xff000024; - - gint_exc(EXPEVT); - while(1); + gint_exc_panic(code); +} + +/* gint_panic_set(): Change the panic handler function */ +void gint_panic_set(GNORETURN void (*panic)(uint32_t code)) +{ + gint_exc_panic = panic; +} + +/* gint_exc_catch(): Set a function to catch exceptions */ +void gint_exc_catch(int (*handler)(uint32_t code)) +{ + gint_exc_catcher = handler; +} + +/* gint_exc_skip(): Skip pending exception instructions */ +void gint_exc_skip(int instructions) +{ + uint32_t spc; + + /* Increase SPC by 2 bytes per instruction */ + __asm__("stc spc, %0" : "=r"(spc)); + spc += 2 * instructions; + __asm__("ldc %0, spc" :: "r"(spc)); } diff --git a/src/core/exch.s b/src/core/exch.s new file mode 100644 index 0000000..a2f8105 --- /dev/null +++ b/src/core/exch.s @@ -0,0 +1,53 @@ +.global _gint_exch_tlbh +.section .gint.exch_tlbh +.align 4 + +_gint_exch_tlbh: + sts.l pr, @-r15 + mov.l r8, @-r15 + + mov.l .expevt, r8 + + /* Panic if the catcher is NULL */ + mov.l .catcher, r0 + mov.l @r0, r0 + tst r0, r0 + bt panic + + /* Leave if the catcher returns zero */ + jsr @r0 + mov.l @r8, r4 + tst r0, r0 + bt end + +panic: + /* RTE to the panic function, but manually, so that SPC is preserved */ + mov.l @r8, r4 + ldc r4, r4_bank + + mov.l @r15+, r8 + lds.l @r15+, pr + + /* Here we switch banks so r0..r7 change meaning! */ + stc ssr, r0 + ldc r0, sr + + mov.l .panic, r0 + mov.l @r0, r0 + jmp @r0 + nop + +end: + mov.l @r15+, r8 + lds.l @r15+, pr + rte + nop + +.align 4 + +.expevt: + .long 0xff000024 +.catcher: + .long _gint_exc_catcher +.panic: + .long _gint_exc_panic diff --git a/src/core/start.c b/src/core/start.c index 1120180..d509ba1 100644 --- a/src/core/start.c +++ b/src/core/start.c @@ -134,12 +134,7 @@ int start(int isappli, int optnum) bootlog_mapped(rom, ram); /* Cancel add-in execution if not all pages are mapped */ - if(rom < (uint32_t)&srom) - { - gint_exc(0x1040); - while(1); - return 1; - } + if(rom < (uint32_t)&srom) gint_panic(0x1040); /* Install gint and switch VBR */ gint_install(); diff --git a/src/dma/dma.c b/src/dma/dma.c index 17c918e..195dae0 100644 --- a/src/dma/dma.c +++ b/src/dma/dma.c @@ -152,34 +152,6 @@ void dma_transfer_noint(int channel, dma_size_t size, uint blocks, DMA.OR.NMIF = 0; } -//--- -// Address error handler -//--- - -/* gint_dma_ae(): DMA Address Error handler */ -void gint_dma_ae(void) -{ - gint_exc(0x1020); - - #ifdef FXCG50 - dprint(6, 141, C_BLACK, C_NONE, - "SAR0: %08x DAR0: %08x TCR0: %08x", - DMA.DMA0.SAR, DMA.DMA0.DAR, DMA.DMA0.TCR); - dprint(6, 154, C_BLACK, C_NONE, - "CHCR0: %08x", DMA.DMA0.CHCR); - dprint(6, 167, C_BLACK, C_NONE, - "SAR1: %08x DAR1: %08x TCR1: %08x", - DMA.DMA1.SAR, DMA.DMA1.DAR, DMA.DMA1.TCR); - dprint(6, 180, C_BLACK, C_NONE, - "CHCR1: %08x", DMA.DMA1.CHCR); - dprint(6, 193, C_BLACK, C_NONE, - "DMAOR: %04x", DMA.OR); - dupdate_noint(); - #endif - - while(1); -} - //--- // Initialization //--- diff --git a/src/dma/inth.s b/src/dma/inth.s index 846a28b..e540534 100644 --- a/src/dma/inth.s +++ b/src/dma/inth.s @@ -34,16 +34,19 @@ _inth_dma_te: /* DMA ADDRESS ERROR INTERRUPT HANDLER - 18 BYTES */ _inth_dma_ae: - /* Call a fixed function */ - sts.l pr, @-r15 - mov.l 1f, r1 - jsr @r1 - nop - lds.l @r15+, pr + /* Manually RTE into the panic routine, preserving SPC */ + mov.l 2f, r4 + ldc r4, r4_bank - rte + /* This instruction changes register bank! */ + stc ssr, r1 + ldc r1, sr + + mov.l 1f, r0 + jmp @r0 nop - .zero 14 + .zero 10 -1: .long _gint_dma_ae +1: .long _gint_panic +2: .long 0x1020