| /* |
| * 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 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 |
| |
| 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 ( 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; |
| } |
| else if( xInterruptHandlerTable[ ulVectorNumber ] != NULL ) |
| { |
| /* Already in use by the application. */ |
| xReturn = pdFAIL; |
| } |
| else |
| { |
| xReturn = pdPASS; |
| } |
| |
| return xReturn; |
| } |
| /*-----------------------------------------------------------*/ |
| |
| void vGenerateYieldInterrupt( void ) |
| { |
| __asm volatile( portYIELD_INTERRUPT ); |
| } |