|
@@ -301,25 +301,31 @@ ENTRY(_ex_replaceable)
|
|
|
nop;
|
|
|
|
|
|
ENTRY(_ex_trap_c)
|
|
|
+ /* The only thing that has been saved in this context is
|
|
|
+ * (R7:6,P5:4), ASTAT & SP - don't use anything else
|
|
|
+ */
|
|
|
+
|
|
|
+ GET_PDA(p5, r6);
|
|
|
+
|
|
|
/* Make sure we are not in a double fault */
|
|
|
p4.l = lo(IPEND);
|
|
|
p4.h = hi(IPEND);
|
|
|
r7 = [p4];
|
|
|
CC = BITTST (r7, 5);
|
|
|
if CC jump _double_fault;
|
|
|
+ [p5 + PDA_EXIPEND] = r7;
|
|
|
|
|
|
/* Call C code (trap_c) to handle the exception, which most
|
|
|
* likely involves sending a signal to the current process.
|
|
|
* To avoid double faults, lower our priority to IRQ5 first.
|
|
|
*/
|
|
|
- P5.h = _exception_to_level5;
|
|
|
- P5.l = _exception_to_level5;
|
|
|
+ r7.h = _exception_to_level5;
|
|
|
+ r7.l = _exception_to_level5;
|
|
|
p4.l = lo(EVT5);
|
|
|
p4.h = hi(EVT5);
|
|
|
- [p4] = p5;
|
|
|
+ [p4] = r7;
|
|
|
csync;
|
|
|
|
|
|
- GET_PDA(p5, r6);
|
|
|
#ifndef CONFIG_DEBUG_DOUBLEFAULT
|
|
|
|
|
|
/*
|
|
@@ -349,8 +355,7 @@ ENTRY(_ex_trap_c)
|
|
|
BITCLR(r6, SYSCFG_SSSTEP_P);
|
|
|
SYSCFG = r6;
|
|
|
|
|
|
- /* Disable all interrupts, but make sure level 5 is enabled so
|
|
|
- * we can switch to that level. Save the old mask. */
|
|
|
+ /* Save the current IMASK, since we change in order to jump to level 5 */
|
|
|
cli r6;
|
|
|
[p5 + PDA_EXIMASK] = r6;
|
|
|
|
|
@@ -358,9 +363,21 @@ ENTRY(_ex_trap_c)
|
|
|
p4.h = hi(SAFE_USER_INSTRUCTION);
|
|
|
retx = p4;
|
|
|
|
|
|
+ /* Disable all interrupts, but make sure level 5 is enabled so
|
|
|
+ * we can switch to that level.
|
|
|
+ */
|
|
|
r6 = 0x3f;
|
|
|
sti r6;
|
|
|
|
|
|
+ /* In case interrupts are disabled IPEND[4] (global interrupt disable bit)
|
|
|
+ * clear it (re-enabling interrupts again) by the special sequence of pushing
|
|
|
+ * RETI onto the stack. This way we can lower ourselves to IVG5 even if the
|
|
|
+ * exception was taken after the interrupt handler was called but before it
|
|
|
+ * got a chance to enable global interrupts itself.
|
|
|
+ */
|
|
|
+ [--sp] = reti;
|
|
|
+ sp += 4;
|
|
|
+
|
|
|
raise 5;
|
|
|
jump.s _bfin_return_from_exception;
|
|
|
ENDPROC(_ex_trap_c)
|
|
@@ -420,47 +437,52 @@ ENDPROC(_double_fault)
|
|
|
ENTRY(_exception_to_level5)
|
|
|
SAVE_ALL_SYS
|
|
|
|
|
|
- GET_PDA(p4, r7); /* Fetch current PDA */
|
|
|
- r6 = [p4 + PDA_RETX];
|
|
|
+ GET_PDA(p5, r7); /* Fetch current PDA */
|
|
|
+ r6 = [p5 + PDA_RETX];
|
|
|
[sp + PT_PC] = r6;
|
|
|
|
|
|
- r6 = [p4 + PDA_SYSCFG];
|
|
|
+ r6 = [p5 + PDA_SYSCFG];
|
|
|
[sp + PT_SYSCFG] = r6;
|
|
|
|
|
|
- /* Restore interrupt mask. We haven't pushed RETI, so this
|
|
|
- * doesn't enable interrupts until we return from this handler. */
|
|
|
- r6 = [p4 + PDA_EXIMASK];
|
|
|
- sti r6;
|
|
|
-
|
|
|
/* Restore the hardware error vector. */
|
|
|
- P5.h = _evt_ivhw;
|
|
|
- P5.l = _evt_ivhw;
|
|
|
+ r7.h = _evt_ivhw;
|
|
|
+ r7.l = _evt_ivhw;
|
|
|
p4.l = lo(EVT5);
|
|
|
p4.h = hi(EVT5);
|
|
|
- [p4] = p5;
|
|
|
+ [p4] = r7;
|
|
|
csync;
|
|
|
|
|
|
- p2.l = lo(IPEND);
|
|
|
- p2.h = hi(IPEND);
|
|
|
- csync;
|
|
|
- r0 = [p2]; /* Read current IPEND */
|
|
|
- [sp + PT_IPEND] = r0; /* Store IPEND */
|
|
|
+#ifdef CONFIG_DEBUG_DOUBLEFAULT
|
|
|
+ /* Now that we have the hardware error vector programmed properly
|
|
|
+ * we can re-enable interrupts (IPEND[4]), so if the _trap_c causes
|
|
|
+ * another hardware error, we can catch it (self-nesting).
|
|
|
+ */
|
|
|
+ [--sp] = reti;
|
|
|
+ sp += 4;
|
|
|
+#endif
|
|
|
+
|
|
|
+ r7 = [p5 + PDA_EXIPEND] /* Read the IPEND from the Exception state */
|
|
|
+ [sp + PT_IPEND] = r7; /* Store IPEND onto the stack */
|
|
|
|
|
|
r0 = sp; /* stack frame pt_regs pointer argument ==> r0 */
|
|
|
SP += -12;
|
|
|
call _trap_c;
|
|
|
SP += 12;
|
|
|
|
|
|
-#ifdef CONFIG_DEBUG_DOUBLEFAULT
|
|
|
- /* Grab ILAT */
|
|
|
- p2.l = lo(ILAT);
|
|
|
- p2.h = hi(ILAT);
|
|
|
- r0 = [p2];
|
|
|
- r1 = 0x20; /* Did I just cause anther HW error? */
|
|
|
- r0 = r0 & r1;
|
|
|
- CC = R0 == R1;
|
|
|
- if CC JUMP _double_fault;
|
|
|
-#endif
|
|
|
+ /* If interrupts were off during the exception (IPEND[4] = 1), turn them off
|
|
|
+ * before we return.
|
|
|
+ */
|
|
|
+ CC = BITTST(r7, EVT_IRPTEN_P)
|
|
|
+ if !CC jump 1f;
|
|
|
+ /* this will load a random value into the reti register - but that is OK,
|
|
|
+ * since we do restore it to the correct value in the 'RESTORE_ALL_SYS' macro
|
|
|
+ */
|
|
|
+ sp += -4;
|
|
|
+ reti = [sp++];
|
|
|
+1:
|
|
|
+ /* restore the interrupt mask (IMASK) */
|
|
|
+ r6 = [p5 + PDA_EXIMASK];
|
|
|
+ sti r6;
|
|
|
|
|
|
call _ret_from_exception;
|
|
|
RESTORE_ALL_SYS
|