interrupts: save caller-saved registers in main handler

This is an obvious requirement for the interrupt routine, which was
forgotten and only surfaced when I used a timer callback started with
multiplications in an innocent add-in. r0..r7 are saved automatically,
which leaves pr, gbr, mach et macl susceptible to corruption by the
interrupt handler.
This commit is contained in:
Lephe 2020-05-06 16:37:44 +02:00
parent 61da7debc8
commit 2cdf925f94
No known key found for this signature in database
GPG key ID: 1BBA026E13FC0495
8 changed files with 61 additions and 21 deletions

View file

@ -97,21 +97,24 @@ int gint_intlevel(int intid, int level);
list of event codes and their associated interrupts. list of event codes and their associated interrupts.
The handler function must be an interrupt handler: it must not raise The handler function must be an interrupt handler: it must not raise
exceptions, must end with 'rte', and it will use the kernel register bank. exceptions, must end with 'rts', and it will use the kernel register bank.
You read that right, it must end with rts because gint's main handler saves
registers besides the automated r0..r7. Do *not* use 'rte' in the handler.
For convenience I allow any block size to be loaded as an interrupt handler, For convenience I allow any block size to be loaded as an interrupt handler,
but it should really be a multiple of 0x20 bytes and not override other but it should really be a multiple of 0x20 bytes and not override other
handlers. If it's not written in assembler, then you're likely doing handlers. If it's not written in assembler, then you're likely doing
something wrong, even with __attribute__((interrupt_handler)). something wrong, especially with __attribute__((interrupt_handler)) which
uses rte.
It is common for interrupt handlers to have a few bytes of data, such as the It is common for interrupt handlers to have a few bytes of data, such as the
address of a callback function. gint often stores this data in the last address of a callback function. gint often stores this data in the last
bytes of the block. This function returns the VBR address of the block to bytes of the block. This function returns the VBR address of the block which
allow the caller to edit the parameters. has just been installed, to allow the caller to edit the parameters later.
@event_code Identifier of the interrupt block @event_code Identifier of the interrupt block
@handler Address of handler function @handler Address of handler function
@size How many bytes to copy @size How many bytes to copy
Returns the VBR address where the handlers was installed. */ Returns the VBR address where the handlers was installed. */
void *gint_inthandler(int event_code, const void *handler, size_t size); void *gint_inthandler(int event_code, void const *handler, size_t size);
#endif /* GINT_GINT */ #endif /* GINT_GINT */

View file

@ -3,6 +3,7 @@
#include <gint/clock.h> #include <gint/clock.h>
#include <gint/mpu/dma.h> #include <gint/mpu/dma.h>
#include <gint/defs/attributes.h> #include <gint/defs/attributes.h>
#include <gint/hardware.h>
#define dprint(x, y, ...) dprint(x, y, C_BLACK, C_NONE, __VA_ARGS__) #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) #define dtext(x, y, str) dtext (x, y, str, C_BLACK, C_NONE)
@ -16,6 +17,9 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
uint32_t PC; uint32_t PC;
__asm__("stc spc, %0" : "=r"(PC)); __asm__("stc spc, %0" : "=r"(PC));
uint32_t SGR = 0xffffffff;
if(isSH4()) __asm__("stc sgr, %0" : "=r"(SGR));
dfont(NULL); dfont(NULL);
dclear(C_WHITE); dclear(C_WHITE);
@ -42,6 +46,7 @@ GNORETURN static void gint_default_panic(GUNUSED uint32_t code)
dprint(1, 17, " PC :%08x", PC); dprint(1, 17, " PC :%08x", PC);
dprint(1, 25, "TEA :%08x", TEA); dprint(1, 25, "TEA :%08x", TEA);
dprint(1, 33, "TRA :%08x", TRA); dprint(1, 33, "TRA :%08x", TRA);
if(isSH4()) dprint(1, 41, "SGR :%08x", SGR);
dtext(1, 49, "The add-in crashed."); dtext(1, 49, "The add-in crashed.");
dtext(1, 57, "Please reset the calc"); dtext(1, 57, "Please reset the calc");

View file

@ -58,6 +58,6 @@ void *gint_inthandler(int event_code, const void *handler, size_t size)
/* Prevent overriding the entry gate */ /* Prevent overriding the entry gate */
if(event_code < 0) return NULL; if(event_code < 0) return NULL;
void *dest = (void *)&gint_vbr + event_code + 0x620; void *dest = (void *)&gint_vbr + event_code + 0x640;
return memcpy(dest, handler, size); return memcpy(dest, handler, size);
} }

View file

@ -54,9 +54,16 @@ _gint_inth_7305:
1: .long 0xff000028 1: .long 0xff000028
#endif #endif
/* SH7305-TYPE INTERRUPT HANDLER ENTRY - 20 BYTES */ /* SH7305-TYPE INTERRUPT HANDLER ENTRY - 40 BYTES */
_gint_inth_7305: _gint_inth_7305:
/* Save caller-saved registers which might currently be in use by the
interrupted function, as we don't know what the callback will do */
sts.l pr, @-r15
stc.l gbr, @-r15
sts.l mach, @-r15
sts.l macl, @-r15
/* Get the event code from the INTEVT register */ /* Get the event code from the INTEVT register */
mov.l 1f, r0 mov.l 1f, r0
mov.l @r0, r0 mov.l @r0, r0
@ -66,19 +73,34 @@ _gint_inth_7305:
shll8 r1 shll8 r1
sub r1, r0 sub r1, r0
/* Add the distance to the first entry and jump */ /* Add the distance to the first entry and jump as a subroutine */
add #16, r0 add #40, r0
braf r0 bsrf r0
nop nop
.zero 12 /* Restore caller-saved registers */
lds.l @r15+, macl
lds.l @r15+, mach
ldc.l @r15+, gbr
lds.l @r15+, pr
rte
nop
.zero 24
1: .long 0xff000028 1: .long 0xff000028
#ifdef FX9860G #ifdef FX9860G
/* SH7705-TYPE INTERRUT HANDLER ENTRY - 32 BYTES */ /* SH7705-TYPE INTERRUT HANDLER ENTRY - 52 BYTES */
_gint_inth_7705: _gint_inth_7705:
/* Save caller-saved registers as before */
sts.l pr, @-r15
stc.l gbr, @-r15
sts.l mach, @-r15
sts.l macl, @-r15
/* Get the event code from the INTEVT2 register */ /* Get the event code from the INTEVT2 register */
mov.l 1f, r0 mov.l 1f, r0
mov.l @r0, r0 /* r0 = old_code */ mov.l @r0, r0 /* r0 = old_code */
@ -93,10 +115,20 @@ _gint_inth_7705:
shld r3, r0 /* r0 = new_code - 0x400 */ shld r3, r0 /* r0 = new_code - 0x400 */
/* Add the distance between nop and the first entry, and jump */ /* Add the distance between nop and the first entry, and jump */
add #8, r0 add #32, r0
braf r0 bsrf r0
nop nop
/* Restore saved registers */
lds.l @r15+, mach
lds.l @r15+, macl
ldc.l @r15+, gbr
lds.l @r15+, pr
rte
nop
.zero 12
1: .long 0xa4000000 /* INTEVT2 register */ 1: .long 0xa4000000 /* INTEVT2 register */
2: .long _inth_remap 2: .long _inth_remap

View file

@ -85,7 +85,7 @@ void gint_install(void)
/* Load the event handler entry points into memory */ /* Load the event handler entry points into memory */
memcpy(vbr + 0x100, gint_exch_tlbh, exch_tlbh_size); memcpy(vbr + 0x100, gint_exch_tlbh, exch_tlbh_size);
memcpy(vbr + 0x400, gint_exch_tlbh, exch_tlbh_size); memcpy(vbr + 0x400, gint_exch_tlbh, exch_tlbh_size);
memcpy(vbr + 0x600, inth_entry, 32); memcpy(vbr + 0x600, inth_entry, 64);
/* Time to switch VBR and roll! */ /* Time to switch VBR and roll! */
system_vbr = gint_setvbr((uint32_t)vbr, lock); system_vbr = gint_setvbr((uint32_t)vbr, lock);

View file

@ -25,7 +25,7 @@ _inth_dma_te:
and r2, r0 and r2, r0
mov.w r0, @r1 mov.w r0, @r1
rte rts
nop nop
1: .long 0 /* CHCR, set dynamically */ 1: .long 0 /* CHCR, set dynamically */

View file

@ -54,7 +54,7 @@ _inth_rtc_pri_helper:
.end: .end:
lds.l @r15+, pr lds.l @r15+, pr
rte rts
nop nop
.zero 8 .zero 8

View file

@ -81,7 +81,7 @@ _inth_tmu_1:
.end: .end:
lds.l @r15+, pr lds.l @r15+, pr
rte rts
nop nop
.zero 12 .zero 12
@ -176,7 +176,7 @@ _inth_etmu_help:
nop nop
lds.l @r15+, pr lds.l @r15+, pr
rte rts
nop nop
.zero 18 .zero 18
@ -199,10 +199,10 @@ _inth_etmux:
/* Offset from VBR where extra timer 2 is located: /* Offset from VBR where extra timer 2 is located:
- 0x600 to reach the interrupt handlers - 0x600 to reach the interrupt handlers
- 0x020 to jump over the entry gate - 0x040 to jump over the entry gate
- 0x840 to reach the handler of ETMU2 - 0x840 to reach the handler of ETMU2
- 0x004 to skip its first instructions (the size is hardcoded) */ - 0x004 to skip its first instructions (the size is hardcoded) */
1: .long 0xe64 1: .long 0xe84
.id_etmux: .id_etmux:
.long 0 /* Timer ID */ .long 0 /* Timer ID */