mirror of
https://git.planet-casio.com/Lephenixnoir/gint.git
synced 2024-12-28 04:23:36 +01:00
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:
parent
eca05ec64c
commit
76c82beec6
4 changed files with 58 additions and 7 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue