diff --git a/include/gint/defs/call.h b/include/gint/defs/call.h index 8870884..cd442cb 100644 --- a/include/gint/defs/call.h +++ b/include/gint/defs/call.h @@ -144,6 +144,19 @@ typedef struct { /* GINT_CALL_NULL: Empty function call */ #define GINT_CALL_NULL ((gint_call_t){ .function = NULL, .args = {} }) +/* GINT_CALL_FLAG(): Build an indirect call to an odd address + + This is used as a workaround to have an extra flag in gint_call_t structures + without altering their size and exploits the fact that SuperH code has to be + aligned on even addresses. + It is up to the caller to notice the presence of the flag and realign the + address properly. + + This flag is currently only checked by gint_inth_callback to specify that the + called function will take the interrupt context as its first argument. */ +#define GINT_CALL_FLAG(func, ...) \ + GINT_CALL((void*)((uint32_t)(func) | 1), __VA_ARGS__) + /* gint_call(): Perform an indirect call */ static GINLINE int gint_call(gint_call_t cb) { diff --git a/include/gint/gint.h b/include/gint/gint.h index 13b7e1d..0ff0f75 100644 --- a/include/gint/gint.h +++ b/include/gint/gint.h @@ -105,10 +105,29 @@ static GINLINE void *gint_inthandler(int code, void const *h, size_t size) { .callback: .long _gint_inth_callback + The gint_call_t object can be built with GINT_CALL_FLAG to specify that the + called function should get a pointer to a gint_inth_callback_context_t + strcture as its first argument. + @call Address of a gint_call_t object Returns the return value of the callback. */ extern int (*gint_inth_callback)(gint_call_t const *call); +/* gint_inth_callback_context_t: Context of the interrupted function when an + interrupt is handled by gint_inth_callback. */ +typedef struct { + uint32_t ssr; + uint32_t spc; + uint32_t r7; + uint32_t r6; + uint32_t r5; + uint32_t r4; + uint32_t r3; + uint32_t r2; + uint32_t r1; + uint32_t r0; +} gint_inth_callback_context_t; + /* gint_set_quit_handler(): Setup a call to be invoked when leaving the add-in This function sets up the provided GINT_CALL() to be invoked when the diff --git a/include/gint/intc.h b/include/gint/intc.h index 9b117f0..ee4a9e3 100644 --- a/include/gint/intc.h +++ b/include/gint/intc.h @@ -130,6 +130,11 @@ void *intc_handler(int event_code, void const *handler, size_t size); the numerous constraints of intc_handler(), at the cost of always going back to userspace and a small time overhead. + Since gint_inth_callback is used to do the heavy lifting of setting up a sane + context for C code, the gint_call_t object can be built with GINT_CALL_FLAG + if the called function expects a gint_inth_callback_context_t structure as its + first argument. + @event_code Identifier of the interrupt block @function Function to use as a handler Returns true on success, false if the event code is invalid. */ diff --git a/src/kernel/inth.S b/src/kernel/inth.S index 4823cd7..1634211 100644 --- a/src/kernel/inth.S +++ b/src/kernel/inth.S @@ -209,11 +209,15 @@ _gint_inth_callback_reloc: stc.l r7_bank, @-r15 stc.l spc, @-r15 stc.l ssr, @-r15 + /* We backup the address of the gint_inth_callback_context_t built on + the stack to the user bank. */ + ldc r15, r4_bank + stc.l sr, @-r15 /* Save some values to user bank; once we enable interrupts, the kernel bank might be overwritten at any moment. */ - ldc r4, r0_bank + ldc r4, r3_bank /* Enable interrupts and go back to user bank. On SH4, SR.IMASK is set to the level of the current interrupt, which makes sure we can only @@ -243,15 +247,25 @@ _gint_inth_callback_reloc: .load_sr: ldc r1, sr - /* We are now in the user bank with r0 set. Perform the call. We want + /* We are now in the user bank with r3 set. Perform the call. We want to forward the return value to kernel bank, but this bank can be changed at any moment since interrupts are enabled. */ sts.l pr, @-r15 - mov.l @(4, r0), r4 - mov.l @(8, r0), r5 - mov.l @(12, r0), r6 - mov.l @(16, r0), r7 - mov.l @r0, r0 + + /* We only set r4 to the value of the first argument in the gint_call_t + if the address is even (i.e. GINT_CALL_FLAG() was not used). */ + mov.l @r3, r0 + tst #1, r0 + bf .do_not_set_r4 + mov.l @(4, r3), r4 +.do_not_set_r4: + /* And we make sure to realign the address in case it was odd. */ + mov #0xfe, r2 + and r2, r0 + + mov.l @(8, r3), r5 + mov.l @(12, r3), r6 + mov.l @(16, r3), r7 jsr @r0 nop lds.l @r15+, pr