intc: allow user-space handlers to access the interrupted context

This workaround using a gint_call_t with an odd address is not realy the
cleanest idea but it helps keep the existing intc_generic_handler in
the 32 bytes size limit of the VBR space.
This commit is contained in:
redoste 2023-05-27 18:36:15 +02:00
parent eca05ec64c
commit 76c82beec6
Signed by: redoste
SSH Key Fingerprint: SHA256:Y1z/cbVN8OVoFPXzVgklSqoZnyQQ/PQM8YwhMtxMk90
4 changed files with 58 additions and 7 deletions

View File

@ -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)
{

View File

@ -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

View File

@ -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. */

View File

@ -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