Mercurial > ecos-v3_0-branch
diff packages/hal/sparclite/arch/current/src/context.S @ 2:443894e2e912 ecos-v1_2_1-release
Block commit of eCos version 1.2.1
| author | jlarmour |
|---|---|
| date | Tue, 11 May 1999 12:24:34 +0000 |
| parents | |
| children | ece80412419a |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/hal/sparclite/arch/current/src/context.S @@ -0,0 +1,366 @@ +/*============================================================================= +// +// context.S +// +// SPARClite context switch code +// +//============================================================================= +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Cygnus eCos Public License +// Version 1.0 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://sourceware.cygnus.com/ecos +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Cygnus Operating System, released +// September 30, 1998. +// +// The Initial Developer of the Original Code is Cygnus. Portions created +// by Cygnus are Copyright (C) 1998,1999 Cygnus Solutions. All Rights Reserved. +// ------------------------------------------- +// +//###COPYRIGHTEND#### +//============================================================================= +//#####DESCRIPTIONBEGIN#### +// +// Author(s): nickg, gthomas, hmt +// Contributors: nickg, gthomas, hmt +// Date: 1998-12-15 +// Purpose: SPARClite context switch code +// Description: This file contains implementations of the thread context +// switch routines. It also contains the longjmp() and setjmp() +// routines. +// +//####DESCRIPTIONEND#### +// +//===========================================================================*/ + +#include <pkgconf/hal.h> + +#include <cyg/hal/vectors.h> + +#define DELAYS_AFTER_WRPSR_SAME_WINDOW +#define DELAYS_AFTER_WRWIM + + .text + +! ------------------------------------------------------------------------------ +! hal_thread_switch_context +! Switch thread contexts +! %o0 = address of sp of next thread to execute +! %o1 = address of sp save location of current thread + + .global hal_thread_switch_context +hal_thread_switch_context: + + ! First take the stack down to make room for the saved register + ! state, including a window save area at the base. Leave the + ! current window save area undisturbed. It is unused within the + ! save but will become current again when we continue in this + ! context. This lets us do this whole piece of work without + ! diabling interrupts for too long, since, for example, we can + ! lower the stack atomically with one instruction: + sub %sp, SAVE_REGS_SIZE, %sp + st %sp, [ %o1 ] ! return SP for this thread + + std %l0, [%sp + 0 * 4] ! save L & I registers + std %l2, [%sp + 2 * 4] + std %l4, [%sp + 4 * 4] + std %l6, [%sp + 6 * 4] + + std %i0, [%sp + 8 * 4] + std %i2, [%sp + 10 * 4] + std %i4, [%sp + 12 * 4] + std %i6, [%sp + 14 * 4] + +#ifdef CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM + st %o7, [%sp + 31 * 4] ! save only the return address + ! and no need to preserve %o0 even though it is restored +#else // save a maximal context + st %g1, [%sp + 17 * 4] ! save G & O registers + std %g2, [%sp + 18 * 4] + std %g4, [%sp + 20 * 4] + std %g6, [%sp + 22 * 4] + + std %o0, [%sp + 24 * 4] + std %o2, [%sp + 26 * 4] + std %o4, [%sp + 28 * 4] + std %o6, [%sp + 30 * 4] +#endif // !CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM + ! and save the CWP in %g0 save place + rd %psr, %g7 + st %g7, [%sp + 16 * 4] + + ! Now load the destination thread by dropping through + ! to hal_thread_load_context + +! ------------------------------------------------------------------------------ +! hal_thread_load_context +! Load thread context +! %o0 = address of sp of next thread to execute +! Note that this function is also the second half of hal_thread_switch_context +! and is simply dropped into from it. + + .global hal_thread_load_context +hal_thread_load_context: + + ! Here, we are a leaf routine but with slightly odd properties. + ! The stack is still the callers at this point but the register + ! set is up for grabs. So we can use globals: + + ld [ %o0 ], %g7 ! Get the next saved SP + + ! DISABLE INTERRUPTS *ONLY* NOT TRAPS + rd %psr, %g6 + or %g6, 0xfe0, %g5 ! PIL up to 15 leave traps enabled + wr %g5, %psr + + ! force out all our callers register sets onto the stack + ! if necessary: the system will handily take care of this for + ! us as follows: + save %sp, -16 * 4, %sp ! need all these to preserve + save %sp, -16 * 4, %sp ! the linked list property... + save %sp, -16 * 4, %sp + save %sp, -16 * 4, %sp + save %sp, -16 * 4, %sp + save %sp, -16 * 4, %sp + ! six of these is correct; a seventh would force out the current + ! set that was already saved manually above. Note that minimal + ! space is allowed on stack for locals and ins in case this + ! sequence itself gets interrupted and recurses too deep. + + ! now select the new window with traps disabled... + ! preserve ET, clear CWP + andn %g6, __WINBITS_MAXIMAL, %g6 + andn %g6, 0x20, %g5 ! clear ET also into %g5 + + ! get new CWP from %g0 save place... + ld [%g7 + 16 * 4], %g4 ! %g7 holds the new stack pointer + and %g4, __WINBITS, %g4 ! preserve CWP bits + + ! calculate a new WIM... + add %g4, 1, %g3 ! required invalid window number + and %g3, __WINBITS, %g3 ! modulo 8 + mov 1, %g2 + sll %g2, %g3, %g2 ! converted to a mask for the WIM + + ! DISABLE INTERRUPTS + wr %g5, %g4, %psr ! set CWP to new window, disable traps + wr %g2, %wim ! and WIM to new value + nop + nop + nop + + ! Must do this atomically so that the registers match the stack. + ! After locals and ins are loaded, we are conformant to the PCS + ! so can re-enable interrupts. + mov %g7, %sp ! target sp in situ (%sp = %o6) + + ldd [%sp + 0 * 4], %l0 ! restore L & I registers + ldd [%sp + 2 * 4], %l2 + ldd [%sp + 4 * 4], %l4 + ldd [%sp + 6 * 4], %l6 + + ldd [%sp + 8 * 4], %i0 + ldd [%sp + 10 * 4], %i2 + ldd [%sp + 12 * 4], %i4 + ldd [%sp + 14 * 4], %i6 + + ! ENABLE INTERRUPTS + wr %g6, %g4, %psr ! set new CWP and old ET + nop + nop + nop + + ! now load the rest of the context; we can be interrupted here, but + ! that is OK, other state will be preserved in that case... + +#ifdef CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM + ld [%sp + 24 * 4], %o0 ! must load the initial argument +#else // restore a maximal context + ld [%sp + 17 * 4], %g1 + ldd [%sp + 18 * 4], %g2 + ldd [%sp + 20 * 4], %g4 + ldd [%sp + 22 * 4], %g6 + + ldd [%sp + 24 * 4], %o0 + ldd [%sp + 26 * 4], %o2 + ldd [%sp + 28 * 4], %o4 +#endif // !CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM + ! %o6 = %sp, already set up + ld [%sp + 31 * 4], %o7 ! "return" address + + retl + add %sp, SAVE_REGS_SIZE, %sp ! and set the stack back + ! to its entrant value + +! ------------------------------------------------------------------------------ +! HAL longjmp, setjmp implementations + +!FUNC_START(hal_setjmp) + .global hal_setjmp +hal_setjmp: + ! Treat this as a leaf routine, may as well. + ! %o0 is the address of the buffer. + + std %l0, [%o0 + 0 * 4] ! save L & I registers + std %l2, [%o0 + 2 * 4] + std %l4, [%o0 + 4 * 4] + std %l6, [%o0 + 6 * 4] + + std %i0, [%o0 + 8 * 4] + std %i2, [%o0 + 10 * 4] + std %i4, [%o0 + 12 * 4] + std %i6, [%o0 + 14 * 4] + +#ifdef CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM + std %o6, [%o0 + 30 * 4] ! just save %sp and return address +#else // save a maximal context + st %g1, [%o0 + 17 * 4] ! save G & O registers + std %g2, [%o0 + 18 * 4] + std %g4, [%o0 + 20 * 4] + std %g6, [%o0 + 22 * 4] + + std %o0, [%o0 + 24 * 4] + std %o2, [%o0 + 26 * 4] + std %o4, [%o0 + 28 * 4] + std %o6, [%o0 + 30 * 4] +#endif // !CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM + + ! and save the CWP in %g0 save place + rd %psr, %g7 + st %g7, [%o0 + 16 * 4] + + ! DISABLE INTERRUPTS *ONLY* NOT TRAPS + or %g7, 0xfe0, %g6 ! PIL up to 15 leave traps enabled + wr %g6, %psr + + ! force out all our callers register sets onto the stack + ! if necessary: the system will handily take care of this for + ! us as follows: + save %sp, -16 * 4, %sp ! need all these to preserve + save %sp, -16 * 4, %sp ! the linked list property... + save %sp, -16 * 4, %sp + save %sp, -16 * 4, %sp + save %sp, -16 * 4, %sp + save %sp, -16 * 4, %sp + ! six of these is correct; a seventh would force out the current + ! set that was already saved manually above. Note that minimal + ! space is allowed on stack for locals and ins in case this + ! sequence itself gets interrupted and recurses too deep. + + ! (after all, we are about to call deeper not shallower, otherwise + ! using setjmp is inappropriate) + + ! ENABLE INTERRUPTS + wr %g7, %psr ! set CWP back to as-was + nop + nop + nop + +#ifndef CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM + ldd [%o0 + 22 * 4], %g6 ! preserve %g7 and %g6 +#endif + + retl ! ret and return zero to indicate + mov 0, %o0 ! not longjumped-to + +! hal_longjmp loads state from arg0 and returns arg1 +!FUNC_START(hal_longjmp) + .global hal_longjmp +hal_longjmp: + ! This is kind of a leaf routine, it returns elsewhere + ! %o0 is the address of the buffer. + ! %o1 is the value to return in %o0 (since we are a leaf) + + mov %o0, %g7 ! keep the pointer handy + mov %o1, %g1 ! and the return value + ! now select the new window with traps disabled... + rd %psr, %g6 + ! preserve ET, clear CWP + andn %g6, __WINBITS_MAXIMAL, %g6 + andn %g6, 0x20, %g5 ! clear ET also into %g5 + + ! get new CWP from %g0 save place... + ld [%g7 + 16 * 4], %g4 ! %g7 holds the new stack pointer + and %g4, __WINBITS, %g4 ! preserve CWP bits + + ! calculate a new WIM... + add %g4, 1, %g3 ! required invalid window number + and %g3, __WINBITS, %g3 ! modulo 8 + mov 1, %g2 + sll %g2, %g3, %g2 ! converted to a mask for the WIM + + ! DISABLE INTERRUPTS + wr %g5, %g4, %psr ! set CWP to new window, disable traps + wr %g2, %wim ! and WIM to new value + nop + nop + nop + + ! Must do this atomically so that the registers match the stack. + ! After locals and ins are loaded, we are conformant to the PCS + ! so can re-enable interrupts. + + ldd [%g7 + 0 * 4], %l0 ! restore L & I registers + ldd [%g7 + 2 * 4], %l2 + ldd [%g7 + 4 * 4], %l4 + ldd [%g7 + 6 * 4], %l6 + + ldd [%g7 + 8 * 4], %i0 + ldd [%g7 + 10 * 4], %i2 + ldd [%g7 + 12 * 4], %i4 + ldd [%g7 + 14 * 4], %i6 + + ld [%g7 + 30 * 4], %sp ! %o6 = %sp, set up now so as to conform + ! to PCS and so be interruptible + ! ENABLE INTERRUPTS + wr %g6, %g4, %psr ! set new CWP and old ET + nop + nop + nop + + ! now load the rest of the context; we can be interrupted here, but + ! that is OK, other state will be preserved in that case... +#ifdef CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM + ! we are not preserving globals... + ! %o6 = %sp, already set up + ld [%g7 + 31 * 4], %o7 ! "return" address + retl ! %g1 still holds the return value + mov %g1, %o0 + +#else // load a maximal context + mov %g7, %o0 ! original pointer was in %o0 anyway + + ldd [%o0 + 18 * 4], %g2 + ldd [%o0 + 20 * 4], %g4 + ldd [%o0 + 22 * 4], %g6 + + ld [%o0 + 25 * 4], %o1 ! %o0 = original pointer + ldd [%o0 + 26 * 4], %o2 + ldd [%o0 + 28 * 4], %o4 + ! %o6 = %sp, already set up + ld [%o0 + 31 * 4], %o7 ! "return" address + + ! %g1 still holds the return value; want to get this into %o0 + ! and restore %g1 from the saved state; %o0 is the state pointer: + ! g1 = R, o0 = P + xor %o0, %g1, %g1 ! g1 = R^P, o0 = P + xor %o0, %g1, %o0 ! g1 = R^P, o0 = R + xor %o0, %g1, %g1 ! g1 = P, o0 = R all done + + retl + ld [%g1 + 17 * 4], %g1 ! and finally restore %g1 +#endif // !CYGDBG_HAL_COMMON_CONTEXT_SAVE_MINIMUM + +! ------------------------------------------------------------------------------ +! end of context.S + + +
