core tmu: improve reliability of interrupt handlers

This change includes three reliability improvements in handlers:

1. TMU handlers now actively check for the UNF flag to go low rather
   than expecting it to do so right away.
2. CPUOPM.INTMU is now set so that IMASK it updated at every interrupt
   (which is absolutely required for nested interrupts!).
3. gint_inth_callback() no longer performs transfers between user bank
   and kernel bank while in user bank, because this is when interrupts
   are enabled and thus likely to corrupt the kernel bank; rather, it
   now does it while in kernel bank with interrupts disabled.
This commit is contained in:
Lephe 2020-06-20 09:32:48 +02:00
parent 91fd2e5e6a
commit 9cf2f9fe97
Signed by: Lephenixnoir
GPG Key ID: 1BBA026E13FC0495
5 changed files with 78 additions and 20 deletions

View File

@ -20,6 +20,9 @@
Returns the previous VBR address. */
uint32_t gint_setvbr(uint32_t vbr, void (*configure)(void));
void gint_setcpuopm(uint32_t CPUOPM);
uint32_t gint_getcpuopm(void);
/* gint_exch(): Exception handler */
void gint_exch(void);
/* gint_tlbh(): TLB miss handler */

View File

@ -184,28 +184,35 @@ _gint_inth_callback:
stc.l ssr, @-r15
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 r5, r4_bank
/* Enable interrupts and go back to user bank. SR.IMASK is already set
to the level of the current interrupt, which makes sure we can only
be re-interrupted by something with a higher priority. */
mov.l .SR_clear_RB_BL, r1
stc sr, r0
mov.l .SR_clear_RB_BL, r1
and r1, r0
ldc r0, sr
/* We are now in the user bank, but we can still use r0..r7 as the
important values have been saved on the stack. Call back. */
stc r4_bank, r0
/* We are now in the user bank with r0 and r4 set. Call back. 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
jsr @r0
stc r5_bank, r4
nop
lds.l @r15+, pr
/* We want to forward the return value to the system bank */
ldc r0, r0_bank
/* Restore the previous status register and the registers of the
interrupted procedure. */
interrupted procedure. Restoring sr gets us back to system bank with
interrupts disabled. */
ldc.l @r15+, sr
/* We can now pull the return value since interrupts are disabled */
stc r0_bank, r0
ldc.l @r15+, ssr
ldc.l @r15+, spc
ldc.l @r15+, r7_bank

View File

@ -37,6 +37,7 @@ typedef struct
{
uint16_t iprs[12];
uint8_t masks[13];
uint32_t CPUOPM;
} GPACKED(2) gint_core_ctx;
@ -57,6 +58,8 @@ GMAPPED static void gint_ctx_save(gint_core_ctx *ctx)
uint8_t *IMR = (void *)SH7305_INTC.MSK;
for(int i = 0; i < 13; i++, IMR += 4)
ctx->masks[i] = *IMR;
ctx->CPUOPM = gint_getcpuopm();
}
}
@ -82,6 +85,8 @@ GMAPPED static void gint_ctx_restore(gint_core_ctx *ctx)
*IMCR = 0xff;
*IMR = ctx->masks[i];
}
gint_setcpuopm(ctx->CPUOPM);
}
}
@ -100,6 +105,8 @@ GMAPPED static void lock(void)
*(SH7705_INTC.IPRS[i]) = 0x0000;
else for(int i = 0; i < 12; i++)
SH7305_INTC.IPRS[2 * i] = 0x0000;
gint_setcpuopm(gint_getcpuopm() | 0x00000008);
}
/* gint_install() - install and start gint */

View File

@ -3,6 +3,9 @@
*/
.global _gint_setvbr
.global _gint_setcpuopm
.global _gint_getcpuopm
.section .gint.mapped
/* gint_setvbr()
@ -47,3 +50,35 @@ _gint_setvbr:
mov r9, r0
rts
mov.l @r15+, r9
.text
/* gint_setcpuopm()
Changes the CPUOPM value and executes an ICBI to register the change.
@r4 New value of CPUOPM (uint32_t) */
_gint_setcpuopm:
/* Set CPUOPM as requested */
mov.l 1f, r0
mov.l r4, @r0
/* Read CPUOPM again */
mov.l @r0, r5
/* Invalidate a cache address */
mov #-96, r0
shll16 r0
shll8 r0
icbi @r0
rts
nop
_gint_getcpuopm:
mov.l 1f, r0
rts
mov.l @r0, r0
.align 4
1: .long 0xff2f0000

View File

@ -45,23 +45,23 @@ _inth_tmu_0:
sts.l pr, @-r15
mov.l r1, @-r15
/* Load the TCR address and clear the interrupt flag */
/* Load the TCR address */
mov.l .mask, r3
not r3, r4
mov.l @(8, r0), r1
mov.w @r1, r2
/* Clear the interrupt flag */
1: mov.w @r1, r2
tst r4, r2
and r3, r2
mov.w r2, @r1
bf 1b
/* Invoke the callback function and pass the argument */
/* Prepare callback and jump to second section */
mov.l .gint_inth_callback_1, r1
mov.l @r0, r4
jsr @r1
mov.l @(4, r0), r5
/* Jump to second section */
mov.l .timer_stop_1, r1
bra .shared2
mov.l @r15+, r4
mov.l @(4, r0), r5
/* SECOND GATE - TMU1 entry and stop timer */
_inth_tmu_1:
@ -71,9 +71,15 @@ _inth_tmu_1:
/*** This is the second shared section ***/
.shared2:
/* Invoke callback */
jsr @r1
nop
/* Stop the timer if the return value is not zero */
mov.l @r15+, r4
tst r0, r0
bt .shared3
mov.l .timer_stop_1, r1
jsr @r1
nop
@ -82,7 +88,7 @@ _inth_tmu_1:
rts
nop
.zero 12
.zero 4
/* THIRD GATE - TMU2 entry and storage for TMU0 */
_inth_tmu_2:
@ -104,7 +110,7 @@ _inth_tmu_2:
_inth_tmu_storage:
.mask:
.long 0x0000feff
.long 0xfffffeff
.timer_stop_1:
.long _timer_stop