The new name better reflects that this file contains all private nanokernel APIs that are used by various kernel subsystems. Change-Id: I4c258d582e93753eec9e575fdb5f9f2109417a0f Signed-off-by: Allan Stephens <allan.stephens@windriver.com>
391 lines
12 KiB
ArmAsm
391 lines
12 KiB
ArmAsm
/* swap.S - nanokernel swapper code for IA-32 */
|
|
|
|
/*
|
|
* Copyright (c) 2010-2014 Wind River Systems, Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1) Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2) Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3) Neither the name of Wind River Systems nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
DESCRIPTION
|
|
This module implements the _Swap() routine for the IA-32 architecture.
|
|
|
|
Note that the file include/nanokernel/x86/swapstk.h defines
|
|
a representation of the save stack frame generated by _Swap() in order
|
|
to generate offsets (in the form of absolute symbols) for consumption by
|
|
host tools. Please update swapstk.h if changing the structure of the
|
|
save frame on the stack.
|
|
*/
|
|
|
|
#define _ASMLANGUAGE
|
|
|
|
#include <nano_private.h>
|
|
#include <arch/x86/asm.h>
|
|
#include <offsets.h> /* nanokernel structure offset definitions */
|
|
|
|
/* exports (internal APIs) */
|
|
|
|
GTEXT(_Swap)
|
|
|
|
/* externs */
|
|
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* _Swap - initiate a cooperative context switch
|
|
*
|
|
* The _Swap() routine is invoked by various nanokernel services to effect
|
|
* a cooperative context context switch. Prior to invoking _Swap(), the
|
|
* caller disables interrupts (via irq_lock) and the return 'key'
|
|
* is passed as a parameter to _Swap(). The 'key' actually represents
|
|
* the EFLAGS register prior to disabling interrupts via a 'cli' instruction.
|
|
*
|
|
* Given that _Swap() is called to effect a cooperative context context switch,
|
|
* only the non-volatile integer registers need to be saved in the tCCS of the
|
|
* outgoing context. The restoration of the integer registers of the incoming
|
|
* context depends on whether that context was preemptively context switched
|
|
* out. The INT_ACTIVE and EXC_ACTIVE bits in the tCCS->flags field will signify
|
|
* that the context was preemptively context switched out, and thus both the
|
|
* volatile and non-volatile integer registers need to be restored.
|
|
*
|
|
* The non-volatile registers need to be scrubbed to ensure they contain no
|
|
* sensitive information that could compromise system security. This is to
|
|
* make sure that information will not be leaked from one application to
|
|
* another via these volatile registers.
|
|
*
|
|
* Here, the integer registers (EAX, ECX, EDX) have been scrubbed. Any changes
|
|
* to this routine that alter the values of these registers MUST be reviewed
|
|
* for potential security impacts.
|
|
*
|
|
* Floating point registers are handled using a lazy save/restore
|
|
* mechanism since it's expected relatively few contexts will be created
|
|
* with the USE_FP or USE_SSE option bits. The nanokernel data structure
|
|
* maintains a 'current_fp' field to keep track of the context that "owns"
|
|
* the floating point registers. Floating point registers consist of
|
|
* ST0->ST7 (x87 FPU and MMX registers) and XMM0 -> XMM7.
|
|
*
|
|
* All floating point registers are considered 'volatile' thus they will
|
|
* only be saved/restored when a preemptive context context switch occurs.
|
|
*
|
|
* Floating point registers are currently NOT scrubbed, and are subject to
|
|
* potential security leaks.
|
|
*
|
|
* The scheduling algorithm is simple: schedule the head of the runnable
|
|
* FIBER context list, which is represented by _nanokernel.fiber. If there are
|
|
* no runnable FIBER contexts, then schedule the TASK context represented
|
|
* by _nanokernel.task. The _nanokernel.task field will never be NULL.
|
|
*
|
|
* RETURNS: may contain a return value setup by a call to fiberRtnValueSet()
|
|
*
|
|
* C function prototype:
|
|
*
|
|
* unsigned int _Swap (unsigned int eflags);
|
|
*
|
|
*/
|
|
|
|
SECTION_FUNC(TEXT, _Swap)
|
|
movl $_nanokernel, %eax
|
|
|
|
/*
|
|
* Push all non-volatile registers onto the stack; do not copy
|
|
* any of these registers into the tCCS. Only the 'esp' register
|
|
* after all the pushes have been performed) will be stored in the
|
|
* tCCS.
|
|
*/
|
|
|
|
pushl %edi
|
|
pushl %esi
|
|
pushl %ebx
|
|
pushl %ebp
|
|
|
|
/*
|
|
* Leave slot for eax register when _Swap() needs to return a value;
|
|
* pre-populate slot with ebx's value in case _Swap() does not return
|
|
* a value.
|
|
*/
|
|
|
|
pushl %ebx
|
|
|
|
|
|
/* save esp into tCCS structure */
|
|
|
|
movl __tNANO_current_OFFSET (%eax), %ecx
|
|
movl %esp, __tCCS_coopReg_OFFSET + __tCoopReg_esp_OFFSET (%ecx)
|
|
|
|
|
|
/*
|
|
* Determine what FIBER or TASK context needs to be swapped in.
|
|
* Note that the %eax still contains &_nanokernel.
|
|
*/
|
|
|
|
movl __tNANO_fiber_OFFSET (%eax), %ecx
|
|
testl %ecx, %ecx
|
|
jz swapTask /* Jump if no ready fibers */
|
|
|
|
/* remove the head 'tCCS *' from the runnable context list */
|
|
|
|
movl __tCCS_link_OFFSET (%ecx), %ebx
|
|
movl %ebx, __tNANO_fiber_OFFSET (%eax)
|
|
jmp restoreContext
|
|
|
|
|
|
/*
|
|
* There are no FIBER context in the run queue, thus swap in the
|
|
* TASK context specified via _nanokernel.task. The 'task' field
|
|
* will _never_ be NULL.
|
|
*/
|
|
|
|
BRANCH_LABEL(swapTask)
|
|
movl __tNANO_task_OFFSET (%eax), %ecx
|
|
|
|
/* fall through to 'restoreContext' */
|
|
|
|
|
|
/*
|
|
* At this point, the %ecx register contains the 'tCCS *' of
|
|
* the TASK or FIBER to be swapped in, and %eax still
|
|
* contains &_nanokernel.
|
|
*/
|
|
|
|
BRANCH_LABEL(restoreContext)
|
|
|
|
#ifdef CONFIG_FP_SHARING
|
|
#ifdef CONFIG_AUTOMATIC_FP_ENABLING
|
|
/*
|
|
* Clear the CR0[TS] bit (in the event the current context
|
|
* doesn't have floating point enabled) to prevent the "device not
|
|
* available" exception when executing the subsequent fxsave/fnsave
|
|
* and/or fxrstor/frstor instructions.
|
|
*
|
|
* Indeed, it's possible that none of the aforementioned instructions
|
|
* need to be executed, for example, the incoming context doesn't
|
|
* utilize floating point operations. However, the code responsible
|
|
* for setting the CR0[TS] bit appropriately for the incoming context
|
|
* (just after the 'restoreContext_NoFloatSwap' label) will leverage
|
|
* the fact that the following 'clts' was performed already.
|
|
*/
|
|
|
|
clts
|
|
#endif /* CONFIG_AUTOMATIC_FP_ENABLING */
|
|
|
|
|
|
/*
|
|
* Determine whether the incoming context utilizes non-integer
|
|
* capabilities _and_ whether the context was context switched
|
|
* out preemptively.
|
|
*/
|
|
|
|
testl $USE_FP, __tCCS_flags_OFFSET (%ecx)
|
|
je restoreContext_NoFloatSwap
|
|
|
|
|
|
/*
|
|
* The incoming context uses non-integer capabilities (x87 FPU and/or
|
|
* XMM regs): Was it the last context to use non-integer capabilities?
|
|
* If so, there there is no need to restore the non-integer context.
|
|
*/
|
|
|
|
movl __tNANO_current_fp_OFFSET (%eax), %ebx
|
|
cmpl %ebx, %ecx
|
|
je restoreContext_NoFloatSwap
|
|
|
|
|
|
/*
|
|
* The incoming context uses non-integer capabilities (x87 FPU and/or
|
|
* XMM regs) and it was _not_ the last context to use the non-integer
|
|
* capabilities: Check whether the current FP context actually needs
|
|
* to be saved before swapping in the context of the incoming context
|
|
*/
|
|
|
|
testl %ebx, %ebx
|
|
jz restoreContext_NoFloatSave
|
|
|
|
|
|
/*
|
|
* The incoming context uses non-integer capabilities (x87 FPU and/or
|
|
* XMM regs) and it was _not_ the last context to use the non-integer
|
|
* capabilities _and_ the current FP context needs to be saved.
|
|
*
|
|
* Given that the ST[0] -> ST[7] and XMM0 -> XMM7 registers are all
|
|
* 'volatile', only save the registers if the "current FP context"
|
|
* was preemptively context switched.
|
|
*/
|
|
|
|
testl $INT_OR_EXC_MASK, __tCCS_flags_OFFSET (%ebx)
|
|
je restoreContext_NoFloatSave
|
|
|
|
|
|
#ifdef CONFIG_SSE
|
|
testl $USE_SSE, __tCCS_flags_OFFSET (%ebx)
|
|
je x87FloatSave
|
|
|
|
/*
|
|
* 'fxsave' does NOT perform an implicit 'fninit', therefore issue an
|
|
* 'fninit' to ensure a "clean" FPU state for the incoming context
|
|
* (for the case when the fxrstor is not executed).
|
|
*/
|
|
|
|
fxsave __tCCS_preempFloatReg_OFFSET (%ebx)
|
|
fninit
|
|
jmp floatSaveDone
|
|
|
|
BRANCH_LABEL(x87FloatSave)
|
|
#endif /* CONFIG_SSE */
|
|
|
|
/* 'fnsave' performs an implicit 'fninit' after saving state! */
|
|
|
|
fnsave __tCCS_preempFloatReg_OFFSET (%ebx)
|
|
|
|
/* fall through to 'floatSaveDone' */
|
|
|
|
BRANCH_LABEL(floatSaveDone)
|
|
BRANCH_LABEL(restoreContext_NoFloatSave)
|
|
|
|
/*********************************************************
|
|
* Restore floating point context of the incoming context.
|
|
*********************************************************/
|
|
|
|
/*
|
|
* Again, given that the ST[0] -> ST[7] and XMM0 -> XMM7 registers are
|
|
* all 'volatile', only restore the registers if the incoming
|
|
* context was previously preemptively context switched out.
|
|
*/
|
|
|
|
testl $INT_OR_EXC_MASK, __tCCS_flags_OFFSET (%ecx)
|
|
je restoreContext_NoFloatRestore
|
|
|
|
#ifdef CONFIG_SSE
|
|
testl $USE_SSE, __tCCS_flags_OFFSET (%ecx)
|
|
je x87FloatRestore
|
|
|
|
fxrstor __tCCS_preempFloatReg_OFFSET (%ecx)
|
|
jmp floatRestoreDone
|
|
|
|
BRANCH_LABEL(x87FloatRestore)
|
|
|
|
#endif /* CONFIG_SSE */
|
|
|
|
frstor __tCCS_preempFloatReg_OFFSET (%ecx)
|
|
|
|
/* fall through to 'floatRestoreDone' */
|
|
|
|
BRANCH_LABEL(floatRestoreDone)
|
|
BRANCH_LABEL(restoreContext_NoFloatRestore)
|
|
|
|
/* record that the incoming context "owns" the non-integer registers */
|
|
|
|
movl %ecx, __tNANO_current_fp_OFFSET (%eax)
|
|
|
|
|
|
/*
|
|
* Branch point when none of the non-integer registers need to be
|
|
* swapped either due to a) the incoming context does not
|
|
* USE_FP | USE_SSE, or b) the incoming context is the same as
|
|
* the last context that utilized the non-integer registers.
|
|
*/
|
|
|
|
BRANCH_LABEL(restoreContext_NoFloatSwap)
|
|
|
|
#ifdef CONFIG_AUTOMATIC_FP_ENABLING
|
|
/*
|
|
* Leave CR0[TS] clear if incoming context utilizes "floating point"
|
|
* instructions
|
|
*/
|
|
|
|
testl $USE_FP, __tCCS_flags_OFFSET (%ecx)
|
|
jne CROHandlingDone
|
|
|
|
/*
|
|
* The incoming context does NOT currently utilize "floating point"
|
|
* instructions, so set CR0[TS] to ensure the "device not available"
|
|
* exception occurs on the first attempt to access a x87 FPU, MMX,
|
|
* or XMM register.
|
|
*/
|
|
|
|
movl %cr0, %edx
|
|
orl $0x8, %edx
|
|
movl %edx, %cr0
|
|
|
|
BRANCH_LABEL(CROHandlingDone)
|
|
|
|
#endif /* CONFIG_AUTOMATIC_FP_ENABLING */
|
|
#endif /* CONFIG_FP_SHARING */
|
|
|
|
|
|
|
|
|
|
/* update _nanokernel.current to reflect incoming context */
|
|
|
|
movl %ecx, __tNANO_current_OFFSET (%eax)
|
|
|
|
/* recover task/fiber stack pointer from tCCS */
|
|
|
|
movl __tCCS_coopReg_OFFSET + __tCoopReg_esp_OFFSET (%ecx), %esp
|
|
|
|
|
|
/* load return value from a possible fiberRtnValueSet() */
|
|
|
|
popl %eax
|
|
|
|
/* pop the non-volatile registers from the stack */
|
|
|
|
popl %ebp
|
|
popl %ebx
|
|
popl %esi
|
|
popl %edi
|
|
|
|
/*
|
|
* For a non-preemptive context switch, it is checked that the volatile
|
|
* integer registers have the following values:
|
|
*
|
|
* 1. ECX - points to the task's own CCS structure.
|
|
* 2. EDX - contains the flags field of the task's own CCS structure.
|
|
* 3. EAX - may contain one of the two values:
|
|
* (a) the return value for _Swap() that was set up by a
|
|
* call to fiberRtnValueSet()
|
|
* (b) same value as EBX, which is non-volatile
|
|
*/
|
|
|
|
/* Utilize the 'eflags' parameter to _Swap() */
|
|
|
|
pushl 4(%esp)
|
|
#ifdef CONFIG_INT_LATENCY_BENCHMARK
|
|
testl $0x200, (%esp)
|
|
jz skipIntLatencyStop
|
|
|
|
/* save %eax since it used as the return value for _Swap */
|
|
pushl %eax
|
|
/* interrupts are being reenabled, stop accumulating time */
|
|
call _int_latency_stop
|
|
/* restore _Swap's %eax */
|
|
popl %eax
|
|
|
|
BRANCH_LABEL(skipIntLatencyStop)
|
|
#endif
|
|
popfl
|
|
ret
|
|
|