[MIPS] SMTC: Close tiny holes in the SMTC IPI replay system.
[pandora-kernel.git] / include / asm-mips / stackframe.h
index ed33366..4c37c4e 100644 (file)
@@ -6,6 +6,7 @@
  * Copyright (C) 1994, 95, 96, 99, 2001 Ralf Baechle
  * Copyright (C) 1994, 1995, 1996 Paul M. Antoine.
  * Copyright (C) 1999 Silicon Graphics, Inc.
+ * Copyright (C) 2007  Maciej W. Rozycki
  */
 #ifndef _ASM_STACKFRAME_H
 #define _ASM_STACKFRAME_H
 #else
                MFC0    k0, CP0_CONTEXT
 #endif
-#if defined(CONFIG_BUILD_ELF64) || (defined(CONFIG_64BIT) && __GNUC__ < 4)
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
+               lui     k1, %hi(kernelsp)
+#else
                lui     k1, %highest(kernelsp)
                daddiu  k1, %higher(kernelsp)
                dsll    k1, 16
                daddiu  k1, %hi(kernelsp)
                dsll    k1, 16
-#else
-               lui     k1, %hi(kernelsp)
 #endif
                LONG_SRL        k0, PTEBASE_SHIFT
                LONG_ADDU       k1, k0
                .endm
 #else
                .macro  get_saved_sp    /* Uniprocessor variation */
-#if defined(CONFIG_BUILD_ELF64) || (defined(CONFIG_64BIT) && __GNUC__ < 4)
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
+               lui     k1, %hi(kernelsp)
+#else
                lui     k1, %highest(kernelsp)
                daddiu  k1, %higher(kernelsp)
                dsll    k1, k1, 16
                daddiu  k1, %hi(kernelsp)
                dsll    k1, k1, 16
-#else
-               lui     k1, %hi(kernelsp)
 #endif
                LONG_L  k1, %lo(kernelsp)(k1)
                .endm
                .set    reorder
                /* Called from user mode, new stack. */
                get_saved_sp
+#ifndef CONFIG_CPU_DADDI_WORKAROUNDS
 8:             move    k0, sp
                PTR_SUBU sp, k1, PT_SIZE
+#else
+               .set    at=k0
+8:             PTR_SUBU k1, PT_SIZE
+               .set    noat
+               move    k0, sp
+               move    sp, k1
+#endif
                LONG_S  k0, PT_R29(sp)
                LONG_S  $3, PT_R3(sp)
                /*
 #ifdef CONFIG_MIPS_MT_SMTC
                .set    mips32r2
                /*
-                * This may not really be necessary if ints are already
-                * inhibited here.
+                * We need to make sure the read-modify-write
+                * of Status below isn't perturbed by an interrupt
+                * or cross-TC access, so we need to do at least a DMT,
+                * protected by an interrupt-inhibit. But setting IXMT
+                * also creates a few-cycle window where an IPI could
+                * be queued and not be detected before potentially
+                * returning to a WAIT or user-mode loop. It must be
+                * replayed.
+                *
+                * We're in the middle of a context switch, and
+                * we can't dispatch it directly without trashing
+                * some registers, so we'll try to detect this unlikely
+                * case and program a software interrupt in the VPE,
+                * as would be done for a cross-VPE IPI.  To accomodate
+                * the handling of that case, we're doing a DVPE instead
+                * of just a DMT here to protect against other threads.
+                * This is a lot of cruft to cover a tiny window.
+                * If you can find a better design, implement it!
+                *
                 */
                mfc0    v0, CP0_TCSTATUS
                ori     v0, TCSTATUS_IXMT
                mtc0    v0, CP0_TCSTATUS
                _ehb
-               DMT     5                               # dmt a1
+               DVPE    5                               # dvpe a1
                jal     mips_ihb
 #endif /* CONFIG_MIPS_MT_SMTC */
                mfc0    a0, CP0_STATUS
  */
                LONG_L  v1, PT_TCSTATUS(sp)
                _ehb
-               mfc0    v0, CP0_TCSTATUS
+               mfc0    a0, CP0_TCSTATUS
                andi    v1, TCSTATUS_IXMT
-               /* We know that TCStatua.IXMT should be set from above */
-               xori    v0, v0, TCSTATUS_IXMT
-               or      v0, v0, v1
-               mtc0    v0, CP0_TCSTATUS
-               _ehb
-               andi    a1, a1, VPECONTROL_TE
+               bnez    v1, 0f
+
+/*
+ * We'd like to detect any IPIs queued in the tiny window
+ * above and request an software interrupt to service them
+ * when we ERET.
+ *
+ * Computing the offset into the IPIQ array of the executing
+ * TC's IPI queue in-line would be tedious.  We use part of
+ * the TCContext register to hold 16 bits of offset that we
+ * can add in-line to find the queue head.
+ */
+               mfc0    v0, CP0_TCCONTEXT
+               la      a2, IPIQ
+               srl     v0, v0, 16
+               addu    a2, a2, v0
+               LONG_L  v0, 0(a2)
+               beqz    v0, 0f
+/*
+ * If we have a queue, provoke dispatch within the VPE by setting C_SW1
+ */
+               mfc0    v0, CP0_CAUSE
+               ori     v0, v0, C_SW1
+               mtc0    v0, CP0_CAUSE
+0:
+               /*
+                * This test should really never branch but
+                * let's be prudent here.  Having atomized
+                * the shared register modifications, we can
+                * now EVPE, and must do so before interrupts
+                * are potentially re-enabled.
+                */
+               andi    a1, a1, MVPCONTROL_EVP
                beqz    a1, 1f
-               emt
+               evpe
 1:
+               /* We know that TCStatua.IXMT should be set from above */
+               xori    a0, a0, TCSTATUS_IXMT
+               or      a0, a0, v1
+               mtc0    a0, CP0_TCSTATUS
+               _ehb
+
                .set    mips0
 #endif /* CONFIG_MIPS_MT_SMTC */
                LONG_L  v1, PT_EPC(sp)
                 * and disable interrupts only for the
                 * current TC, using the TCStatus register.
                 */
-               mfc0    t0,CP0_TCSTATUS
+               mfc0    t0, CP0_TCSTATUS
                /* Fortunately CU 0 is in the same place in both registers */
                /* Set TCU0, TMX, TKSU (for later inversion) and IXMT */
                li      t1, ST0_CU0 | 0x08001c00
-               or      t0,t1
+               or      t0, t1
                /* Clear TKSU, leave IXMT */
                xori    t0, 0x00001800
                mtc0    t0, CP0_TCSTATUS
                 * current TC, using the TCStatus register.
                 */
                _ehb
-               mfc0    t0,CP0_TCSTATUS
+               mfc0    t0, CP0_TCSTATUS
                /* Fortunately CU 0 is in the same place in both registers */
                /* Set TCU0, TKSU (for later inversion) and IXMT */
                li      t1, ST0_CU0 | 0x08001c00
-               or      t0,t1
+               or      t0, t1
                /* Clear TKSU *and* IXMT */
                xori    t0, 0x00001c00
                mtc0    t0, CP0_TCSTATUS