2 * FreeRTOS Kernel <DEVELOPMENT BRANCH>
3 * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5 * SPDX-License-Identifier: MIT
7 * Permission is hereby granted, free of charge, to any person obtaining a copy of
8 * this software and associated documentation files (the "Software"), to deal in
9 * the Software without restriction, including without limitation the rights to
10 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11 * the Software, and to permit persons to whom the Software is furnished to do so,
12 * subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 * https://www.FreeRTOS.org
25 * https://github.com/FreeRTOS
29 /* Standard includes. */
32 /* Scheduler includes. */
36 #if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 )
37 /* Check the configuration. */
38 #if ( configMAX_PRIORITIES > 32 )
39 #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.
41 #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
43 #if ( configISR_STACK_SIZE < ( configMINIMAL_STACK_SIZE * 2 ) )
44 #warning configISR_STACK_SIZE is probably too small!
45 #endif /* ( configISR_STACK_SIZE < configMINIMAL_STACK_SIZE * 2 ) */
47 #if ( ( configMAX_API_CALL_INTERRUPT_PRIORITY > portMAX_PRIORITY ) || ( configMAX_API_CALL_INTERRUPT_PRIORITY < 2 ) )
48 #error configMAX_API_CALL_INTERRUPT_PRIORITY must be between 2 and 15
51 #if ( ( configSUPPORT_FPU == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) )
52 #error configSUPPORT_DYNAMIC_ALLOCATION must be set to 1 to use this port with an FPU
55 /* A critical section is exited when the critical section nesting count reaches
57 #define portNO_CRITICAL_NESTING ( ( uint32_t ) 0 )
59 /* Tasks are not created with a floating point context, but can be given a
60 * floating point context after they have been created. A variable is stored as
61 * part of the tasks context that holds portNO_FLOATING_POINT_CONTEXT if the task
62 * does not have an FPU context, or any other value if the task does have an FPU
64 #define portNO_FLOATING_POINT_CONTEXT ( ( StackType_t ) 0 )
66 /* Only the IF bit is set so tasks start with interrupts enabled. */
67 #define portINITIAL_EFLAGS ( 0x200UL )
69 /* Error interrupts are at the highest priority vectors. */
70 #define portAPIC_LVT_ERROR_VECTOR ( 0xfe )
71 #define portAPIC_SPURIOUS_INT_VECTOR ( 0xff )
74 #define portEFLAGS_IF ( 0x200UL )
76 /* FPU context size if FSAVE is used. */
77 #define portFPU_CONTEXT_SIZE_BYTES 108
79 /* The expected size of each entry in the IDT. Used to check structure packing
80 * is set correctly. */
81 #define portEXPECTED_IDT_ENTRY_SIZE 8
83 /* Default flags setting for entries in the IDT. */
84 #define portIDT_FLAGS ( 0x8E )
86 /* This is the lowest possible ISR vector available to application code. */
87 #define portAPIC_MIN_ALLOWABLE_VECTOR ( 0x20 )
89 /* If configASSERT() is defined then the system stack is filled with this value
90 * to allow for a crude stack overflow check. */
91 #define portSTACK_WORD ( 0xecececec )
92 /*-----------------------------------------------------------*/
95 * Starts the first task executing.
97 extern void vPortStartFirstTask( void );
100 * Used to catch tasks that attempt to return from their implementing function.
102 static void prvTaskExitError( void );
105 * Complete one descriptor in the IDT.
107 static void prvSetInterruptGate( uint8_t ucNumber,
108 ISR_Handler_t pxHandlerFunction,
112 * The default handler installed in each IDT position.
114 extern void vPortCentralInterruptWrapper( void );
117 * Handler for portYIELD().
119 extern void vPortYieldCall( void );
122 * Configure the APIC to generate the RTOS tick.
124 static void prvSetupTimerInterrupt( void );
127 * Tick interrupt handler.
129 extern void vPortTimerHandler( void );
132 * Check an interrupt vector is not too high, too low, in use by FreeRTOS, or
133 * already in use by the application.
135 static BaseType_t prvCheckValidityOfVectorNumber( uint32_t ulVectorNumber );
137 /*-----------------------------------------------------------*/
139 /* A variable is used to keep track of the critical section nesting. This
140 * variable must be initialised to a non zero value to ensure interrupts don't
141 * inadvertently become unmasked before the scheduler starts. It is set to zero
142 * before the first task starts executing. */
143 volatile uint32_t ulCriticalNesting = 9999UL;
145 /* A structure used to map the various fields of an IDT entry into separate
146 * structure members. */
149 uint16_t usISRLow; /* Low 16 bits of handler address. */
150 uint16_t usSegmentSelector; /* Flat model means this is not changed. */
151 uint8_t ucZero; /* Must be set to zero. */
152 uint8_t ucFlags; /* Flags for this entry. */
153 uint16_t usISRHigh; /* High 16 bits of handler address. */
155 __attribute__( ( packed ) );
156 typedef struct IDTEntry IDTEntry_t;
159 /* Use to pass the location of the IDT to the CPU. */
162 uint16_t usTableLimit;
163 uint32_t ulTableBase; /* The address of the first entry in xInterruptDescriptorTable. */
165 __attribute__( ( __packed__ ) );
166 typedef struct IDTPointer IDTPointer_t;
168 /* The IDT itself. */
169 static __attribute__( ( aligned( 32 ) ) ) IDTEntry_t xInterruptDescriptorTable[ portNUM_VECTORS ];
171 #if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
173 /* A table in which application defined interrupt handlers are stored. These
174 * are called by the central interrupt handler if a common interrupt entry
176 static ISR_Handler_t xInterruptHandlerTable[ portNUM_VECTORS ] = { NULL };
178 #endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
180 #if ( configSUPPORT_FPU == 1 )
182 /* Saved as part of the task context. If pucPortTaskFPUContextBuffer is NULL
183 * then the task does not have an FPU context. If pucPortTaskFPUContextBuffer is
184 * not NULL then it points to a buffer into which the FPU context can be saved. */
185 uint8_t * pucPortTaskFPUContextBuffer __attribute__( ( used ) ) = pdFALSE;
187 #endif /* configSUPPORT_FPU */
189 /* The stack used by interrupt handlers. */
190 static uint32_t ulSystemStack[ configISR_STACK_SIZE ] __attribute__( ( used ) ) = { 0 };
192 /* Don't use the very top of the system stack so the return address
193 * appears as 0 if the debugger tries to unwind the stack. */
194 volatile uint32_t ulTopOfSystemStack __attribute__( ( used ) ) = ( uint32_t ) &( ulSystemStack[ configISR_STACK_SIZE - 5 ] );
196 /* If a yield is requested from an interrupt or from a critical section then
197 * the yield is not performed immediately, and ulPortYieldPending is set to pdTRUE
198 * instead to indicate the yield should be performed at the end of the interrupt
199 * when the critical section is exited. */
200 volatile uint32_t ulPortYieldPending __attribute__( ( used ) ) = pdFALSE;
202 /* Counts the interrupt nesting depth. Used to know when to switch to the
203 * interrupt/system stack and when to save/restore a complete context. */
204 volatile uint32_t ulInterruptNesting __attribute__( ( used ) ) = 0;
206 /*-----------------------------------------------------------*/
209 * See header file for description.
211 StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
212 TaskFunction_t pxCode,
213 void * pvParameters )
215 uint32_t ulCodeSegment;
217 /* Setup the initial stack as expected by the portFREERTOS_INTERRUPT_EXIT macro. */
219 *pxTopOfStack = 0x00;
221 *pxTopOfStack = 0x00;
224 /* Parameters first. */
225 *pxTopOfStack = ( StackType_t ) pvParameters;
228 /* There is nothing to return to so assert if attempting to use the return
230 *pxTopOfStack = ( StackType_t ) prvTaskExitError;
233 /* iret used to start the task pops up to here. */
234 *pxTopOfStack = portINITIAL_EFLAGS;
238 __asm volatile ( "movl %%cs, %0" : "=r" ( ulCodeSegment ) );
239 *pxTopOfStack = ulCodeSegment;
242 /* First instruction in the task. */
243 *pxTopOfStack = ( StackType_t ) pxCode;
246 /* General purpose registers as expected by a POPA instruction. */
247 *pxTopOfStack = 0xEA;
250 *pxTopOfStack = 0xEC;
253 *pxTopOfStack = 0xED1; /* EDX */
256 *pxTopOfStack = 0xEB1; /* EBX */
262 *pxTopOfStack = 0x00; /* EBP */
265 *pxTopOfStack = 0xE5; /* ESI */
268 *pxTopOfStack = 0xeeeeeeee; /* EDI */
270 #if ( configSUPPORT_FPU == 1 )
274 /* Buffer for FPU context, which is initialised to NULL as tasks are not
275 * created with an FPU context. */
276 *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
278 #endif /* configSUPPORT_FPU */
282 /*-----------------------------------------------------------*/
284 static void prvSetInterruptGate( uint8_t ucNumber,
285 ISR_Handler_t pxHandlerFunction,
288 uint16_t usCodeSegment;
289 uint32_t ulBase = ( uint32_t ) pxHandlerFunction;
291 xInterruptDescriptorTable[ ucNumber ].usISRLow = ( uint16_t ) ( ulBase & USHRT_MAX );
292 xInterruptDescriptorTable[ ucNumber ].usISRHigh = ( uint16_t ) ( ( ulBase >> 16UL ) & USHRT_MAX );
294 /* When the flat model is used the CS will never change. */
295 __asm volatile ( "mov %%cs, %0" : "=r" ( usCodeSegment ) );
296 xInterruptDescriptorTable[ ucNumber ].usSegmentSelector = usCodeSegment;
297 xInterruptDescriptorTable[ ucNumber ].ucZero = 0;
298 xInterruptDescriptorTable[ ucNumber ].ucFlags = ucFlags;
300 /*-----------------------------------------------------------*/
302 void vPortSetupIDT( void )
307 #if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
309 for( ulNum = 0; ulNum < portNUM_VECTORS; ulNum++ )
311 /* If a handler has not already been installed on this vector. */
312 if( ( xInterruptDescriptorTable[ ulNum ].usISRLow == 0x00 ) && ( xInterruptDescriptorTable[ ulNum ].usISRHigh == 0x00 ) )
314 prvSetInterruptGate( ( uint8_t ) ulNum, vPortCentralInterruptWrapper, portIDT_FLAGS );
318 #endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
320 /* Set IDT address. */
321 xIDT.ulTableBase = ( uint32_t ) xInterruptDescriptorTable;
322 xIDT.usTableLimit = sizeof( xInterruptDescriptorTable ) - 1;
324 /* Set IDT in CPU. */
325 __asm volatile ( "lidt %0" ::"m" ( xIDT ) );
327 /*-----------------------------------------------------------*/
329 static void prvTaskExitError( void )
331 /* A function that implements a task must not exit or attempt to return to
332 * its caller as there is nothing to return to. If a task wants to exit it
333 * should instead call vTaskDelete( NULL ).
335 * Artificially force an assert() to be triggered if configASSERT() is
336 * defined, then stop here so application writers can catch the error. */
337 configASSERT( ulCriticalNesting == ~0UL );
338 portDISABLE_INTERRUPTS();
344 /*-----------------------------------------------------------*/
346 static void prvSetupTimerInterrupt( void )
348 extern void vPortAPICErrorHandlerWrapper( void );
349 extern void vPortAPICSpuriousHandler( void );
351 /* Initialise LAPIC to a well known state. */
352 portAPIC_LDR = 0xFFFFFFFF;
353 portAPIC_LDR = ( ( portAPIC_LDR & 0x00FFFFFF ) | 0x00000001 );
354 portAPIC_LVT_TIMER = portAPIC_DISABLE;
355 portAPIC_LVT_PERF = portAPIC_NMI;
356 portAPIC_LVT_LINT0 = portAPIC_DISABLE;
357 portAPIC_LVT_LINT1 = portAPIC_DISABLE;
358 portAPIC_TASK_PRIORITY = 0;
360 /* Install APIC timer ISR vector. */
361 prvSetInterruptGate( ( uint8_t ) portAPIC_TIMER_INT_VECTOR, vPortTimerHandler, portIDT_FLAGS );
363 /* Install API error handler. */
364 prvSetInterruptGate( ( uint8_t ) portAPIC_LVT_ERROR_VECTOR, vPortAPICErrorHandlerWrapper, portIDT_FLAGS );
366 /* Install Yield handler. */
367 prvSetInterruptGate( ( uint8_t ) portAPIC_YIELD_INT_VECTOR, vPortYieldCall, portIDT_FLAGS );
369 /* Install spurious interrupt vector. */
370 prvSetInterruptGate( ( uint8_t ) portAPIC_SPURIOUS_INT_VECTOR, vPortAPICSpuriousHandler, portIDT_FLAGS );
372 /* Enable the APIC, mapping the spurious interrupt at the same time. */
373 portAPIC_SPURIOUS_INT = portAPIC_SPURIOUS_INT_VECTOR | portAPIC_ENABLE_BIT;
375 /* Set timer error vector. */
376 portAPIC_LVT_ERROR = portAPIC_LVT_ERROR_VECTOR;
378 /* Set the interrupt frequency. */
379 portAPIC_TMRDIV = portAPIC_DIV_16;
380 portAPIC_TIMER_INITIAL_COUNT = ( ( configCPU_CLOCK_HZ >> 4UL ) / configTICK_RATE_HZ ) - 1UL;
382 /*-----------------------------------------------------------*/
384 BaseType_t xPortStartScheduler( void )
388 /* Some versions of GCC require the -mno-ms-bitfields command line option
389 * for packing to work. */
390 configASSERT( sizeof( struct IDTEntry ) == portEXPECTED_IDT_ENTRY_SIZE );
392 /* Fill part of the system stack with a known value to help detect stack
393 * overflow. A few zeros are left so GDB doesn't get confused unwinding
395 for( xWord = 0; xWord < configISR_STACK_SIZE - 20; xWord++ )
397 ulSystemStack[ xWord ] = portSTACK_WORD;
400 /* Initialise Interrupt Descriptor Table (IDT). */
403 /* Initialise LAPIC and install system handlers. */
404 prvSetupTimerInterrupt();
406 /* Make sure the stack used by interrupts is aligned. */
407 ulTopOfSystemStack &= ~portBYTE_ALIGNMENT_MASK;
409 ulCriticalNesting = 0;
411 /* Enable LAPIC Counter.*/
412 portAPIC_LVT_TIMER = portAPIC_TIMER_PERIODIC | portAPIC_TIMER_INT_VECTOR;
414 /* Sometimes needed. */
415 portAPIC_TMRDIV = portAPIC_DIV_16;
417 /* Should not return from the following function as the scheduler will then
418 * be executing the tasks. */
419 vPortStartFirstTask();
423 /*-----------------------------------------------------------*/
425 void vPortEndScheduler( void )
427 /* Not implemented in ports where there is nothing to return to.
428 * Artificially force an assert. */
429 configASSERT( ulCriticalNesting == 1000UL );
431 /*-----------------------------------------------------------*/
433 void vPortEnterCritical( void )
435 if( ulCriticalNesting == 0 )
437 #if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
439 __asm volatile ( "cli" );
443 portAPIC_TASK_PRIORITY = portMAX_API_CALL_PRIORITY;
444 configASSERT( portAPIC_TASK_PRIORITY == portMAX_API_CALL_PRIORITY );
449 /* Now that interrupts are disabled, ulCriticalNesting can be accessed
450 * directly. Increment ulCriticalNesting to keep a count of how many times
451 * portENTER_CRITICAL() has been called. */
454 /*-----------------------------------------------------------*/
456 void vPortExitCritical( void )
458 if( ulCriticalNesting > portNO_CRITICAL_NESTING )
460 /* Decrement the nesting count as the critical section is being
464 /* If the nesting level has reached zero then all interrupt
465 * priorities must be re-enabled. */
466 if( ulCriticalNesting == portNO_CRITICAL_NESTING )
468 /* Critical nesting has reached zero so all interrupt priorities
469 * should be unmasked. */
470 #if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
472 __asm volatile ( "sti" );
476 portAPIC_TASK_PRIORITY = 0;
480 /* If a yield was pended from within the critical section then
481 * perform the yield now. */
482 if( ulPortYieldPending != pdFALSE )
484 ulPortYieldPending = pdFALSE;
485 __asm volatile ( portYIELD_INTERRUPT );
490 /*-----------------------------------------------------------*/
492 uint32_t ulPortSetInterruptMask( void )
494 volatile uint32_t ulOriginalMask;
496 /* Set mask to max syscall priority. */
497 #if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
499 /* Return whether interrupts were already enabled or not. Pop adjusts
500 * the stack first. */
501 __asm volatile ( "pushf \t\n"
504 : "=rm" ( ulOriginalMask )::"memory" );
506 ulOriginalMask &= portEFLAGS_IF;
510 /* Return original mask. */
511 ulOriginalMask = portAPIC_TASK_PRIORITY;
512 portAPIC_TASK_PRIORITY = portMAX_API_CALL_PRIORITY;
513 configASSERT( portAPIC_TASK_PRIORITY == portMAX_API_CALL_PRIORITY );
515 #endif /* if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY ) */
517 return ulOriginalMask;
519 /*-----------------------------------------------------------*/
521 void vPortClearInterruptMask( uint32_t ulNewMaskValue )
523 #if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
525 if( ulNewMaskValue != pdFALSE )
527 __asm volatile ( "sti" );
532 portAPIC_TASK_PRIORITY = ulNewMaskValue;
533 configASSERT( portAPIC_TASK_PRIORITY == ulNewMaskValue );
535 #endif /* if ( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY ) */
537 /*-----------------------------------------------------------*/
539 #if ( configSUPPORT_FPU == 1 )
541 void vPortTaskUsesFPU( void )
543 /* A task is registering the fact that it needs an FPU context. Allocate a
544 * buffer into which the context can be saved. */
545 pucPortTaskFPUContextBuffer = ( uint8_t * ) pvPortMalloc( portFPU_CONTEXT_SIZE_BYTES );
546 configASSERT( pucPortTaskFPUContextBuffer );
548 /* Initialise the floating point registers. */
549 __asm volatile ( "fninit" );
552 #endif /* configSUPPORT_FPU */
553 /*-----------------------------------------------------------*/
555 void vPortAPICErrorHandler( void )
557 /* Variable to hold the APIC error status for viewing in the debugger. */
558 volatile uint32_t ulErrorStatus = 0;
560 portAPIC_ERROR_STATUS = 0;
561 ulErrorStatus = portAPIC_ERROR_STATUS;
562 ( void ) ulErrorStatus;
564 /* Force an assert. */
565 configASSERT( ulCriticalNesting == ~0UL );
567 /*-----------------------------------------------------------*/
569 #if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
571 void vPortCentralInterruptHandler( uint32_t ulVector )
573 if( ulVector < portNUM_VECTORS )
575 if( xInterruptHandlerTable[ ulVector ] != NULL )
577 ( xInterruptHandlerTable[ ulVector ] )();
581 /* Check for a system stack overflow. */
582 configASSERT( ulSystemStack[ 10 ] == portSTACK_WORD );
583 configASSERT( ulSystemStack[ 12 ] == portSTACK_WORD );
584 configASSERT( ulSystemStack[ 14 ] == portSTACK_WORD );
587 #endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
588 /*-----------------------------------------------------------*/
590 #if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
592 BaseType_t xPortRegisterCInterruptHandler( ISR_Handler_t pxHandler,
593 uint32_t ulVectorNumber )
597 xReturn = prvCheckValidityOfVectorNumber( ulVectorNumber );
599 if( xReturn != pdFAIL )
601 /* Save the handler passed in by the application in the vector number
602 * passed in. The addresses are then called from the central interrupt
604 xInterruptHandlerTable[ ulVectorNumber ] = pxHandler;
610 #endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
611 /*-----------------------------------------------------------*/
613 BaseType_t xPortInstallInterruptHandler( ISR_Handler_t pxHandler,
614 uint32_t ulVectorNumber )
618 xReturn = prvCheckValidityOfVectorNumber( ulVectorNumber );
620 if( xReturn != pdFAIL )
622 taskENTER_CRITICAL();
624 /* Update the IDT to include the application defined handler. */
625 prvSetInterruptGate( ( uint8_t ) ulVectorNumber, ( ISR_Handler_t ) pxHandler, portIDT_FLAGS );
632 /*-----------------------------------------------------------*/
634 static BaseType_t prvCheckValidityOfVectorNumber( uint32_t ulVectorNumber )
638 /* Check validity of vector number. */
639 if( ulVectorNumber >= portNUM_VECTORS )
644 else if( ulVectorNumber < portAPIC_MIN_ALLOWABLE_VECTOR )
649 else if( ulVectorNumber == portAPIC_TIMER_INT_VECTOR )
651 /* In use by FreeRTOS. */
654 else if( ulVectorNumber == portAPIC_YIELD_INT_VECTOR )
656 /* In use by FreeRTOS. */
659 else if( ulVectorNumber == portAPIC_LVT_ERROR_VECTOR )
661 /* In use by FreeRTOS. */
664 else if( ulVectorNumber == portAPIC_SPURIOUS_INT_VECTOR )
666 /* In use by FreeRTOS. */
669 #if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
670 else if( xInterruptHandlerTable[ ulVectorNumber ] != NULL )
672 /* Already in use by the application. */
675 #endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
683 /*-----------------------------------------------------------*/
685 void vGenerateYieldInterrupt( void )
687 __asm volatile ( portYIELD_INTERRUPT );