zephyr/arch/x86/core/swap.S
Allan Stephens c29d41cab7 Rename nanokernel's nanok.h to nano_private.h
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>
2016-02-05 20:14:26 -05:00

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