mirror of
				https://github.com/FreeRTOS/FreeRTOS-Kernel.git
				synced 2025-11-04 11:09:01 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			689 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			689 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * FreeRTOS Kernel <DEVELOPMENT BRANCH>
 | 
						|
 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 | 
						|
 *
 | 
						|
 * SPDX-License-Identifier: MIT
 | 
						|
 *
 | 
						|
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 | 
						|
 * this software and associated documentation files (the "Software"), to deal in
 | 
						|
 * the Software without restriction, including without limitation the rights to
 | 
						|
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 | 
						|
 * the Software, and to permit persons to whom the Software is furnished to do so,
 | 
						|
 * subject to the following conditions:
 | 
						|
 *
 | 
						|
 * The above copyright notice and this permission notice shall be included in all
 | 
						|
 * copies or substantial portions of the Software.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
						|
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 | 
						|
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 | 
						|
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 | 
						|
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 | 
						|
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
						|
 *
 | 
						|
 * https://www.FreeRTOS.org
 | 
						|
 * https://github.com/FreeRTOS
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
/* Standard includes. */
 | 
						|
#include <limits.h>
 | 
						|
 | 
						|
/* Scheduler includes. */
 | 
						|
#include "FreeRTOS.h"
 | 
						|
#include "task.h"
 | 
						|
 | 
						|
#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 )
 | 
						|
    /* Check the configuration. */
 | 
						|
    #if ( configMAX_PRIORITIES > 32 )
 | 
						|
        #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32.  It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice.
 | 
						|
    #endif
 | 
						|
#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
 | 
						|
 | 
						|
#if ( configISR_STACK_SIZE < ( configMINIMAL_STACK_SIZE * 2 ) )
 | 
						|
    #warning configISR_STACK_SIZE is probably too small!
 | 
						|
#endif /* ( configISR_STACK_SIZE < configMINIMAL_STACK_SIZE * 2 ) */
 | 
						|
 | 
						|
#if ( ( configMAX_API_CALL_INTERRUPT_PRIORITY > portMAX_PRIORITY ) || ( configMAX_API_CALL_INTERRUPT_PRIORITY < 2 ) )
 | 
						|
    #error configMAX_API_CALL_INTERRUPT_PRIORITY must be between 2 and 15
 | 
						|
#endif
 | 
						|
 | 
						|
#if ( ( configSUPPORT_FPU == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) )
 | 
						|
    #error configSUPPORT_DYNAMIC_ALLOCATION must be set to 1 to use this port with an FPU
 | 
						|
#endif
 | 
						|
 | 
						|
/* A critical section is exited when the critical section nesting count reaches
 | 
						|
 * this value. */
 | 
						|
#define portNO_CRITICAL_NESTING          ( ( uint32_t ) 0 )
 | 
						|
 | 
						|
/* Tasks are not created with a floating point context, but can be given a
 | 
						|
 * floating point context after they have been created.  A variable is stored as
 | 
						|
 * part of the tasks context that holds portNO_FLOATING_POINT_CONTEXT if the task
 | 
						|
 * does not have an FPU context, or any other value if the task does have an FPU
 | 
						|
 * context. */
 | 
						|
#define portNO_FLOATING_POINT_CONTEXT    ( ( StackType_t ) 0 )
 | 
						|
 | 
						|
/* Only the IF bit is set so tasks start with interrupts enabled. */
 | 
						|
#define portINITIAL_EFLAGS               ( 0x200UL )
 | 
						|
 | 
						|
/* Error interrupts are at the highest priority vectors. */
 | 
						|
#define portAPIC_LVT_ERROR_VECTOR        ( 0xfe )
 | 
						|
#define portAPIC_SPURIOUS_INT_VECTOR     ( 0xff )
 | 
						|
 | 
						|
/* EFLAGS bits. */
 | 
						|
#define portEFLAGS_IF                    ( 0x200UL )
 | 
						|
 | 
						|
/* FPU context size if FSAVE is used. */
 | 
						|
#define portFPU_CONTEXT_SIZE_BYTES       108
 | 
						|
 | 
						|
/* The expected size of each entry in the IDT.  Used to check structure packing
 | 
						|
 * is set correctly. */
 | 
						|
#define portEXPECTED_IDT_ENTRY_SIZE      8
 | 
						|
 | 
						|
/* Default flags setting for entries in the IDT. */
 | 
						|
#define portIDT_FLAGS                    ( 0x8E )
 | 
						|
 | 
						|
/* This is the lowest possible ISR vector available to application code. */
 | 
						|
#define portAPIC_MIN_ALLOWABLE_VECTOR    ( 0x20 )
 | 
						|
 | 
						|
/* If configASSERT() is defined then the system stack is filled with this value
 | 
						|
 * to allow for a crude stack overflow check. */
 | 
						|
#define portSTACK_WORD                   ( 0xecececec )
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
/*
 | 
						|
 * Starts the first task executing.
 | 
						|
 */
 | 
						|
extern void vPortStartFirstTask( void );
 | 
						|
 | 
						|
/*
 | 
						|
 * Used to catch tasks that attempt to return from their implementing function.
 | 
						|
 */
 | 
						|
static void prvTaskExitError( void );
 | 
						|
 | 
						|
/*
 | 
						|
 * Complete one descriptor in the IDT.
 | 
						|
 */
 | 
						|
static void prvSetInterruptGate( uint8_t ucNumber,
 | 
						|
                                 ISR_Handler_t pxHandlerFunction,
 | 
						|
                                 uint8_t ucFlags );
 | 
						|
 | 
						|
/*
 | 
						|
 * The default handler installed in each IDT position.
 | 
						|
 */
 | 
						|
extern void vPortCentralInterruptWrapper( void );
 | 
						|
 | 
						|
/*
 | 
						|
 * Handler for portYIELD().
 | 
						|
 */
 | 
						|
extern void vPortYieldCall( void );
 | 
						|
 | 
						|
/*
 | 
						|
 * Configure the APIC to generate the RTOS tick.
 | 
						|
 */
 | 
						|
static void prvSetupTimerInterrupt( void );
 | 
						|
 | 
						|
/*
 | 
						|
 * Tick interrupt handler.
 | 
						|
 */
 | 
						|
extern void vPortTimerHandler( void );
 | 
						|
 | 
						|
/*
 | 
						|
 * Check an interrupt vector is not too high, too low, in use by FreeRTOS, or
 | 
						|
 * already in use by the application.
 | 
						|
 */
 | 
						|
static BaseType_t prvCheckValidityOfVectorNumber( uint32_t ulVectorNumber );
 | 
						|
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
/* A variable is used to keep track of the critical section nesting.  This
 | 
						|
 * variable must be initialised to a non zero value to ensure interrupts don't
 | 
						|
 * inadvertently become unmasked before the scheduler starts. It is set to zero
 | 
						|
 * before the first task starts executing. */
 | 
						|
volatile uint32_t ulCriticalNesting = 9999UL;
 | 
						|
 | 
						|
/* A structure used to map the various fields of an IDT entry into separate
 | 
						|
 * structure members. */
 | 
						|
struct IDTEntry
 | 
						|
{
 | 
						|
    uint16_t usISRLow;          /* Low 16 bits of handler address. */
 | 
						|
    uint16_t usSegmentSelector; /* Flat model means this is not changed. */
 | 
						|
    uint8_t ucZero;             /* Must be set to zero. */
 | 
						|
    uint8_t ucFlags;            /* Flags for this entry. */
 | 
						|
    uint16_t usISRHigh;         /* High 16 bits of handler address. */
 | 
						|
}
 | 
						|
__attribute__( ( packed ) );
 | 
						|
typedef struct IDTEntry IDTEntry_t;
 | 
						|
 | 
						|
 | 
						|
/* Use to pass the location of the IDT to the CPU. */
 | 
						|
struct IDTPointer
 | 
						|
{
 | 
						|
    uint16_t usTableLimit;
 | 
						|
    uint32_t ulTableBase; /* The address of the first entry in xInterruptDescriptorTable. */
 | 
						|
}
 | 
						|
__attribute__( ( __packed__ ) );
 | 
						|
typedef struct IDTPointer IDTPointer_t;
 | 
						|
 | 
						|
/* The IDT itself. */
 | 
						|
static __attribute__( ( aligned( 32 ) ) ) IDTEntry_t xInterruptDescriptorTable[ portNUM_VECTORS ];
 | 
						|
 | 
						|
#if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
 | 
						|
 | 
						|
/* A table in which application defined interrupt handlers are stored.  These
 | 
						|
 * are called by the central interrupt handler if a common interrupt entry
 | 
						|
 * point it used. */
 | 
						|
static ISR_Handler_t xInterruptHandlerTable[ portNUM_VECTORS ] = { NULL };
 | 
						|
 | 
						|
#endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
 | 
						|
 | 
						|
#if ( configSUPPORT_FPU == 1 )
 | 
						|
 | 
						|
/* Saved as part of the task context.  If pucPortTaskFPUContextBuffer is NULL
 | 
						|
 * then the task does not have an FPU context.  If pucPortTaskFPUContextBuffer is
 | 
						|
 * not NULL then it points to a buffer into which the FPU context can be saved. */
 | 
						|
    uint8_t * pucPortTaskFPUContextBuffer __attribute__( ( used ) ) = pdFALSE;
 | 
						|
 | 
						|
#endif /* configSUPPORT_FPU */
 | 
						|
 | 
						|
/* The stack used by interrupt handlers. */
 | 
						|
static uint32_t ulSystemStack[ configISR_STACK_SIZE ] __attribute__( ( used ) ) = { 0 };
 | 
						|
 | 
						|
/* Don't use the very top of the system stack so the return address
 | 
						|
 * appears as 0 if the debugger tries to unwind the stack. */
 | 
						|
volatile uint32_t ulTopOfSystemStack __attribute__( ( used ) ) = ( uint32_t ) &( ulSystemStack[ configISR_STACK_SIZE - 5 ] );
 | 
						|
 | 
						|
/* If a yield is requested from an interrupt or from a critical section then
 | 
						|
 * the yield is not performed immediately, and ulPortYieldPending is set to pdTRUE
 | 
						|
 * instead to indicate the yield should be performed at the end of the interrupt
 | 
						|
 * when the critical section is exited. */
 | 
						|
volatile uint32_t ulPortYieldPending __attribute__( ( used ) ) = pdFALSE;
 | 
						|
 | 
						|
/* Counts the interrupt nesting depth.  Used to know when to switch to the
 | 
						|
 * interrupt/system stack and when to save/restore a complete context. */
 | 
						|
volatile uint32_t ulInterruptNesting __attribute__( ( used ) ) = 0;
 | 
						|
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
/*
 | 
						|
 * See header file for description.
 | 
						|
 */
 | 
						|
StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
 | 
						|
                                     TaskFunction_t pxCode,
 | 
						|
                                     void * pvParameters )
 | 
						|
{
 | 
						|
    uint32_t ulCodeSegment;
 | 
						|
 | 
						|
    /* Setup the initial stack as expected by the portFREERTOS_INTERRUPT_EXIT macro. */
 | 
						|
 | 
						|
    *pxTopOfStack = 0x00;
 | 
						|
    pxTopOfStack--;
 | 
						|
    *pxTopOfStack = 0x00;
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    /* Parameters first. */
 | 
						|
    *pxTopOfStack = ( StackType_t ) pvParameters;
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    /* There is nothing to return to so assert if attempting to use the return
 | 
						|
     * address. */
 | 
						|
    *pxTopOfStack = ( StackType_t ) prvTaskExitError;
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    /* iret used to start the task pops up to here. */
 | 
						|
    *pxTopOfStack = portINITIAL_EFLAGS;
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    /* CS */
 | 
						|
    __asm volatile ( "movl %%cs, %0" : "=r" ( ulCodeSegment ) );
 | 
						|
    *pxTopOfStack = ulCodeSegment;
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    /* First instruction in the task. */
 | 
						|
    *pxTopOfStack = ( StackType_t ) pxCode;
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    /* General purpose registers as expected by a POPA instruction. */
 | 
						|
    *pxTopOfStack = 0xEA;
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    *pxTopOfStack = 0xEC;
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    *pxTopOfStack = 0xED1; /* EDX */
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    *pxTopOfStack = 0xEB1; /* EBX */
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    /* Hole for ESP. */
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    *pxTopOfStack = 0x00; /* EBP */
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    *pxTopOfStack = 0xE5; /* ESI */
 | 
						|
    pxTopOfStack--;
 | 
						|
 | 
						|
    *pxTopOfStack = 0xeeeeeeee; /* EDI */
 | 
						|
 | 
						|
    #if ( configSUPPORT_FPU == 1 )
 | 
						|
    {
 | 
						|
        pxTopOfStack--;
 | 
						|
 | 
						|
        /* Buffer for FPU context, which is initialised to NULL as tasks are not
 | 
						|
         * created with an FPU context. */
 | 
						|
        *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
 | 
						|
    }
 | 
						|
    #endif /* configSUPPORT_FPU */
 | 
						|
 | 
						|
    return pxTopOfStack;
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
static void prvSetInterruptGate( uint8_t ucNumber,
 | 
						|
                                 ISR_Handler_t pxHandlerFunction,
 | 
						|
                                 uint8_t ucFlags )
 | 
						|
{
 | 
						|
    uint16_t usCodeSegment;
 | 
						|
    uint32_t ulBase = ( uint32_t ) pxHandlerFunction;
 | 
						|
 | 
						|
    xInterruptDescriptorTable[ ucNumber ].usISRLow = ( uint16_t ) ( ulBase & USHRT_MAX );
 | 
						|
    xInterruptDescriptorTable[ ucNumber ].usISRHigh = ( uint16_t ) ( ( ulBase >> 16UL ) & USHRT_MAX );
 | 
						|
 | 
						|
    /* When the flat model is used the CS will never change. */
 | 
						|
    __asm volatile ( "mov %%cs, %0" : "=r" ( usCodeSegment ) );
 | 
						|
    xInterruptDescriptorTable[ ucNumber ].usSegmentSelector = usCodeSegment;
 | 
						|
    xInterruptDescriptorTable[ ucNumber ].ucZero = 0;
 | 
						|
    xInterruptDescriptorTable[ ucNumber ].ucFlags = ucFlags;
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
void vPortSetupIDT( void )
 | 
						|
{
 | 
						|
    uint32_t ulNum;
 | 
						|
    IDTPointer_t xIDT;
 | 
						|
 | 
						|
    #if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
 | 
						|
    {
 | 
						|
        for( ulNum = 0; ulNum < portNUM_VECTORS; ulNum++ )
 | 
						|
        {
 | 
						|
            /* If a handler has not already been installed on this vector. */
 | 
						|
            if( ( xInterruptDescriptorTable[ ulNum ].usISRLow == 0x00 ) && ( xInterruptDescriptorTable[ ulNum ].usISRHigh == 0x00 ) )
 | 
						|
            {
 | 
						|
                prvSetInterruptGate( ( uint8_t ) ulNum, vPortCentralInterruptWrapper, portIDT_FLAGS );
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    #endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
 | 
						|
 | 
						|
    /* Set IDT address. */
 | 
						|
    xIDT.ulTableBase = ( uint32_t ) xInterruptDescriptorTable;
 | 
						|
    xIDT.usTableLimit = sizeof( xInterruptDescriptorTable ) - 1;
 | 
						|
 | 
						|
    /* Set IDT in CPU. */
 | 
						|
    __asm volatile ( "lidt %0" ::"m" ( xIDT ) );
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
static void prvTaskExitError( void )
 | 
						|
{
 | 
						|
    /* A function that implements a task must not exit or attempt to return to
 | 
						|
     * its caller as there is nothing to return to.  If a task wants to exit it
 | 
						|
     * should instead call vTaskDelete( NULL ).
 | 
						|
     *
 | 
						|
     * Artificially force an assert() to be triggered if configASSERT() is
 | 
						|
     * defined, then stop here so application writers can catch the error. */
 | 
						|
    configASSERT( ulCriticalNesting == ~0UL );
 | 
						|
    portDISABLE_INTERRUPTS();
 | 
						|
 | 
						|
    for( ; ; )
 | 
						|
    {
 | 
						|
    }
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
static void prvSetupTimerInterrupt( void )
 | 
						|
{
 | 
						|
    extern void vPortAPICErrorHandlerWrapper( void );
 | 
						|
    extern void vPortAPICSpuriousHandler( void );
 | 
						|
 | 
						|
    /* Initialise LAPIC to a well known state. */
 | 
						|
    portAPIC_LDR = 0xFFFFFFFF;
 | 
						|
    portAPIC_LDR = ( ( portAPIC_LDR & 0x00FFFFFF ) | 0x00000001 );
 | 
						|
    portAPIC_LVT_TIMER = portAPIC_DISABLE;
 | 
						|
    portAPIC_LVT_PERF = portAPIC_NMI;
 | 
						|
    portAPIC_LVT_LINT0 = portAPIC_DISABLE;
 | 
						|
    portAPIC_LVT_LINT1 = portAPIC_DISABLE;
 | 
						|
    portAPIC_TASK_PRIORITY = 0;
 | 
						|
 | 
						|
    /* Install APIC timer ISR vector. */
 | 
						|
    prvSetInterruptGate( ( uint8_t ) portAPIC_TIMER_INT_VECTOR, vPortTimerHandler, portIDT_FLAGS );
 | 
						|
 | 
						|
    /* Install API error handler. */
 | 
						|
    prvSetInterruptGate( ( uint8_t ) portAPIC_LVT_ERROR_VECTOR, vPortAPICErrorHandlerWrapper, portIDT_FLAGS );
 | 
						|
 | 
						|
    /* Install Yield handler. */
 | 
						|
    prvSetInterruptGate( ( uint8_t ) portAPIC_YIELD_INT_VECTOR, vPortYieldCall, portIDT_FLAGS );
 | 
						|
 | 
						|
    /* Install spurious interrupt vector. */
 | 
						|
    prvSetInterruptGate( ( uint8_t ) portAPIC_SPURIOUS_INT_VECTOR, vPortAPICSpuriousHandler, portIDT_FLAGS );
 | 
						|
 | 
						|
    /* Enable the APIC, mapping the spurious interrupt at the same time. */
 | 
						|
    portAPIC_SPURIOUS_INT = portAPIC_SPURIOUS_INT_VECTOR | portAPIC_ENABLE_BIT;
 | 
						|
 | 
						|
    /* Set timer error vector. */
 | 
						|
    portAPIC_LVT_ERROR = portAPIC_LVT_ERROR_VECTOR;
 | 
						|
 | 
						|
    /* Set the interrupt frequency. */
 | 
						|
    portAPIC_TMRDIV = portAPIC_DIV_16;
 | 
						|
    portAPIC_TIMER_INITIAL_COUNT = ( ( configCPU_CLOCK_HZ >> 4UL ) / configTICK_RATE_HZ ) - 1UL;
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
BaseType_t xPortStartScheduler( void )
 | 
						|
{
 | 
						|
    BaseType_t xWord;
 | 
						|
 | 
						|
    /* Some versions of GCC require the -mno-ms-bitfields command line option
 | 
						|
     * for packing to work. */
 | 
						|
    configASSERT( sizeof( struct IDTEntry ) == portEXPECTED_IDT_ENTRY_SIZE );
 | 
						|
 | 
						|
    /* Fill part of the system stack with a known value to help detect stack
 | 
						|
     * overflow.  A few zeros are left so GDB doesn't get confused unwinding
 | 
						|
     * the stack. */
 | 
						|
    for( xWord = 0; xWord < configISR_STACK_SIZE - 20; xWord++ )
 | 
						|
    {
 | 
						|
        ulSystemStack[ xWord ] = portSTACK_WORD;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Initialise Interrupt Descriptor Table (IDT). */
 | 
						|
    vPortSetupIDT();
 | 
						|
 | 
						|
    /* Initialise LAPIC and install system handlers. */
 | 
						|
    prvSetupTimerInterrupt();
 | 
						|
 | 
						|
    /* Make sure the stack used by interrupts is aligned. */
 | 
						|
    ulTopOfSystemStack &= ~portBYTE_ALIGNMENT_MASK;
 | 
						|
 | 
						|
    ulCriticalNesting = 0;
 | 
						|
 | 
						|
    /* Enable LAPIC Counter.*/
 | 
						|
    portAPIC_LVT_TIMER = portAPIC_TIMER_PERIODIC | portAPIC_TIMER_INT_VECTOR;
 | 
						|
 | 
						|
    /* Sometimes needed. */
 | 
						|
    portAPIC_TMRDIV = portAPIC_DIV_16;
 | 
						|
 | 
						|
    /* Should not return from the following function as the scheduler will then
 | 
						|
     * be executing the tasks. */
 | 
						|
    vPortStartFirstTask();
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
void vPortEndScheduler( void )
 | 
						|
{
 | 
						|
    /* Not implemented in ports where there is nothing to return to.
 | 
						|
     * Artificially force an assert. */
 | 
						|
    configASSERT( ulCriticalNesting == 1000UL );
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
void vPortEnterCritical( void )
 | 
						|
{
 | 
						|
    if( ulCriticalNesting == 0 )
 | 
						|
    {
 | 
						|
        #if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
 | 
						|
        {
 | 
						|
            __asm volatile ( "cli" );
 | 
						|
        }
 | 
						|
        #else
 | 
						|
        {
 | 
						|
            portAPIC_TASK_PRIORITY = portMAX_API_CALL_PRIORITY;
 | 
						|
            configASSERT( portAPIC_TASK_PRIORITY == portMAX_API_CALL_PRIORITY );
 | 
						|
        }
 | 
						|
        #endif
 | 
						|
    }
 | 
						|
 | 
						|
    /* Now that interrupts are disabled, ulCriticalNesting can be accessed
 | 
						|
     * directly.  Increment ulCriticalNesting to keep a count of how many times
 | 
						|
     * portENTER_CRITICAL() has been called. */
 | 
						|
    ulCriticalNesting++;
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
void vPortExitCritical( void )
 | 
						|
{
 | 
						|
    if( ulCriticalNesting > portNO_CRITICAL_NESTING )
 | 
						|
    {
 | 
						|
        /* Decrement the nesting count as the critical section is being
 | 
						|
         * exited. */
 | 
						|
        ulCriticalNesting--;
 | 
						|
 | 
						|
        /* If the nesting level has reached zero then all interrupt
 | 
						|
         * priorities must be re-enabled. */
 | 
						|
        if( ulCriticalNesting == portNO_CRITICAL_NESTING )
 | 
						|
        {
 | 
						|
            /* Critical nesting has reached zero so all interrupt priorities
 | 
						|
             * should be unmasked. */
 | 
						|
            #if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
 | 
						|
            {
 | 
						|
                __asm volatile ( "sti" );
 | 
						|
            }
 | 
						|
            #else
 | 
						|
            {
 | 
						|
                portAPIC_TASK_PRIORITY = 0;
 | 
						|
            }
 | 
						|
            #endif
 | 
						|
 | 
						|
            /* If a yield was pended from within the critical section then
 | 
						|
             * perform the yield now. */
 | 
						|
            if( ulPortYieldPending != pdFALSE )
 | 
						|
            {
 | 
						|
                ulPortYieldPending = pdFALSE;
 | 
						|
                __asm volatile ( portYIELD_INTERRUPT );
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
uint32_t ulPortSetInterruptMask( void )
 | 
						|
{
 | 
						|
    volatile uint32_t ulOriginalMask;
 | 
						|
 | 
						|
    /* Set mask to max syscall priority. */
 | 
						|
    #if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
 | 
						|
    {
 | 
						|
        /* Return whether interrupts were already enabled or not.  Pop adjusts
 | 
						|
         * the stack first. */
 | 
						|
        __asm volatile ( "pushf      \t\n"
 | 
						|
                         "pop %0     \t\n"
 | 
						|
                         "cli            "
 | 
						|
                         : "=rm" ( ulOriginalMask )::"memory" );
 | 
						|
 | 
						|
        ulOriginalMask &= portEFLAGS_IF;
 | 
						|
    }
 | 
						|
    #else
 | 
						|
    {
 | 
						|
        /* Return original mask. */
 | 
						|
        ulOriginalMask = portAPIC_TASK_PRIORITY;
 | 
						|
        portAPIC_TASK_PRIORITY = portMAX_API_CALL_PRIORITY;
 | 
						|
        configASSERT( portAPIC_TASK_PRIORITY == portMAX_API_CALL_PRIORITY );
 | 
						|
    }
 | 
						|
    #endif /* if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY ) */
 | 
						|
 | 
						|
    return ulOriginalMask;
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
void vPortClearInterruptMask( uint32_t ulNewMaskValue )
 | 
						|
{
 | 
						|
    #if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
 | 
						|
    {
 | 
						|
        if( ulNewMaskValue != pdFALSE )
 | 
						|
        {
 | 
						|
            __asm volatile ( "sti" );
 | 
						|
        }
 | 
						|
    }
 | 
						|
    #else
 | 
						|
    {
 | 
						|
        portAPIC_TASK_PRIORITY = ulNewMaskValue;
 | 
						|
        configASSERT( portAPIC_TASK_PRIORITY == ulNewMaskValue );
 | 
						|
    }
 | 
						|
    #endif /* if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY ) */
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
#if ( configSUPPORT_FPU == 1 )
 | 
						|
 | 
						|
    void vPortTaskUsesFPU( void )
 | 
						|
    {
 | 
						|
        /* A task is registering the fact that it needs an FPU context.  Allocate a
 | 
						|
         * buffer into which the context can be saved. */
 | 
						|
        pucPortTaskFPUContextBuffer = ( uint8_t * ) pvPortMalloc( portFPU_CONTEXT_SIZE_BYTES );
 | 
						|
        configASSERT( pucPortTaskFPUContextBuffer );
 | 
						|
 | 
						|
        /* Initialise the floating point registers. */
 | 
						|
        __asm volatile ( "fninit" );
 | 
						|
    }
 | 
						|
 | 
						|
#endif /* configSUPPORT_FPU */
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
void vPortAPICErrorHandler( void )
 | 
						|
{
 | 
						|
/* Variable to hold the APIC error status for viewing in the debugger. */
 | 
						|
    volatile uint32_t ulErrorStatus = 0;
 | 
						|
 | 
						|
    portAPIC_ERROR_STATUS = 0;
 | 
						|
    ulErrorStatus = portAPIC_ERROR_STATUS;
 | 
						|
    ( void ) ulErrorStatus;
 | 
						|
 | 
						|
    /* Force an assert. */
 | 
						|
    configASSERT( ulCriticalNesting == ~0UL );
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
#if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
 | 
						|
 | 
						|
    void vPortCentralInterruptHandler( uint32_t ulVector )
 | 
						|
    {
 | 
						|
        if( ulVector < portNUM_VECTORS )
 | 
						|
        {
 | 
						|
            if( xInterruptHandlerTable[ ulVector ] != NULL )
 | 
						|
            {
 | 
						|
                ( xInterruptHandlerTable[ ulVector ] )();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /* Check for a system stack overflow. */
 | 
						|
        configASSERT( ulSystemStack[ 10 ] == portSTACK_WORD );
 | 
						|
        configASSERT( ulSystemStack[ 12 ] == portSTACK_WORD );
 | 
						|
        configASSERT( ulSystemStack[ 14 ] == portSTACK_WORD );
 | 
						|
    }
 | 
						|
 | 
						|
#endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
#if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
 | 
						|
 | 
						|
    BaseType_t xPortRegisterCInterruptHandler( ISR_Handler_t pxHandler,
 | 
						|
                                               uint32_t ulVectorNumber )
 | 
						|
    {
 | 
						|
        BaseType_t xReturn;
 | 
						|
 | 
						|
        xReturn = prvCheckValidityOfVectorNumber( ulVectorNumber );
 | 
						|
 | 
						|
        if( xReturn != pdFAIL )
 | 
						|
        {
 | 
						|
            /* Save the handler passed in by the application in the vector number
 | 
						|
             * passed in.  The addresses are then called from the central interrupt
 | 
						|
             * handler. */
 | 
						|
            xInterruptHandlerTable[ ulVectorNumber ] = pxHandler;
 | 
						|
        }
 | 
						|
 | 
						|
        return xReturn;
 | 
						|
    }
 | 
						|
 | 
						|
#endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
BaseType_t xPortInstallInterruptHandler( ISR_Handler_t pxHandler,
 | 
						|
                                         uint32_t ulVectorNumber )
 | 
						|
{
 | 
						|
    BaseType_t xReturn;
 | 
						|
 | 
						|
    xReturn = prvCheckValidityOfVectorNumber( ulVectorNumber );
 | 
						|
 | 
						|
    if( xReturn != pdFAIL )
 | 
						|
    {
 | 
						|
        taskENTER_CRITICAL();
 | 
						|
        {
 | 
						|
            /* Update the IDT to include the application defined handler. */
 | 
						|
            prvSetInterruptGate( ( uint8_t ) ulVectorNumber, ( ISR_Handler_t ) pxHandler, portIDT_FLAGS );
 | 
						|
        }
 | 
						|
        taskEXIT_CRITICAL();
 | 
						|
    }
 | 
						|
 | 
						|
    return xReturn;
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
static BaseType_t prvCheckValidityOfVectorNumber( uint32_t ulVectorNumber )
 | 
						|
{
 | 
						|
    BaseType_t xReturn;
 | 
						|
 | 
						|
    /* Check validity of vector number. */
 | 
						|
    if( ulVectorNumber >= portNUM_VECTORS )
 | 
						|
    {
 | 
						|
        /* Too high. */
 | 
						|
        xReturn = pdFAIL;
 | 
						|
    }
 | 
						|
    else if( ulVectorNumber < portAPIC_MIN_ALLOWABLE_VECTOR )
 | 
						|
    {
 | 
						|
        /* Too low. */
 | 
						|
        xReturn = pdFAIL;
 | 
						|
    }
 | 
						|
    else if( ulVectorNumber == portAPIC_TIMER_INT_VECTOR )
 | 
						|
    {
 | 
						|
        /* In use by FreeRTOS. */
 | 
						|
        xReturn = pdFAIL;
 | 
						|
    }
 | 
						|
    else if( ulVectorNumber == portAPIC_YIELD_INT_VECTOR )
 | 
						|
    {
 | 
						|
        /* In use by FreeRTOS. */
 | 
						|
        xReturn = pdFAIL;
 | 
						|
    }
 | 
						|
    else if( ulVectorNumber == portAPIC_LVT_ERROR_VECTOR )
 | 
						|
    {
 | 
						|
        /* In use by FreeRTOS. */
 | 
						|
        xReturn = pdFAIL;
 | 
						|
    }
 | 
						|
    else if( ulVectorNumber == portAPIC_SPURIOUS_INT_VECTOR )
 | 
						|
    {
 | 
						|
        /* In use by FreeRTOS. */
 | 
						|
        xReturn = pdFAIL;
 | 
						|
    }
 | 
						|
#if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
 | 
						|
    else if( xInterruptHandlerTable[ ulVectorNumber ] != NULL )
 | 
						|
    {
 | 
						|
        /* Already in use by the application. */
 | 
						|
        xReturn = pdFAIL;
 | 
						|
    }
 | 
						|
#endif  /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
 | 
						|
    else
 | 
						|
    {
 | 
						|
        xReturn = pdPASS;
 | 
						|
    }
 | 
						|
 | 
						|
    return xReturn;
 | 
						|
}
 | 
						|
/*-----------------------------------------------------------*/
 | 
						|
 | 
						|
void vGenerateYieldInterrupt( void )
 | 
						|
{
 | 
						|
    __asm volatile ( portYIELD_INTERRUPT );
 | 
						|
}
 |