xref: /Kernel-v10.6.2/portable/GCC/IA32_flat/port.c (revision ef7b253b56c9788077f5ecd6c9deb4021923d646)
1 /*
2  * FreeRTOS Kernel V10.6.2
3  * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
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.
23  *
24  * https://www.FreeRTOS.org
25  * https://github.com/FreeRTOS
26  *
27  */
28 
29 /* Standard includes. */
30 #include <limits.h>
31 
32 /* Scheduler includes. */
33 #include "FreeRTOS.h"
34 #include "task.h"
35 
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.
40     #endif
41 #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */
42 
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 ) */
46 
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
49 #endif
50 
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
53 #endif
54 
55 /* A critical section is exited when the critical section nesting count reaches
56 this value. */
57 #define portNO_CRITICAL_NESTING         ( ( uint32_t ) 0 )
58 
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
63 context. */
64 #define portNO_FLOATING_POINT_CONTEXT   ( ( StackType_t ) 0 )
65 
66 /* Only the IF bit is set so tasks start with interrupts enabled. */
67 #define portINITIAL_EFLAGS              ( 0x200UL )
68 
69 /* Error interrupts are at the highest priority vectors. */
70 #define portAPIC_LVT_ERROR_VECTOR       ( 0xfe )
71 #define portAPIC_SPURIOUS_INT_VECTOR    ( 0xff )
72 
73 /* EFLAGS bits. */
74 #define portEFLAGS_IF                   ( 0x200UL )
75 
76 /* FPU context size if FSAVE is used. */
77 #define portFPU_CONTEXT_SIZE_BYTES      108
78 
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
82 
83 /* Default flags setting for entries in the IDT. */
84 #define portIDT_FLAGS                   ( 0x8E )
85 
86 /* This is the lowest possible ISR vector available to application code. */
87 #define portAPIC_MIN_ALLOWABLE_VECTOR   ( 0x20 )
88 
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 /*-----------------------------------------------------------*/
93 
94 /*
95  * Starts the first task executing.
96  */
97 extern void vPortStartFirstTask( void );
98 
99 /*
100  * Used to catch tasks that attempt to return from their implementing function.
101  */
102 static void prvTaskExitError( void );
103 
104 /*
105  * Complete one descriptor in the IDT.
106  */
107 static void prvSetInterruptGate( uint8_t ucNumber, ISR_Handler_t pxHandlerFunction, uint8_t ucFlags );
108 
109 /*
110  * The default handler installed in each IDT position.
111  */
112 extern void vPortCentralInterruptWrapper( void );
113 
114 /*
115  * Handler for portYIELD().
116  */
117 extern void vPortYieldCall( void );
118 
119 /*
120  * Configure the APIC to generate the RTOS tick.
121  */
122 static void prvSetupTimerInterrupt( void );
123 
124 /*
125  * Tick interrupt handler.
126  */
127 extern void vPortTimerHandler( void );
128 
129 /*
130  * Check an interrupt vector is not too high, too low, in use by FreeRTOS, or
131  * already in use by the application.
132  */
133 static BaseType_t prvCheckValidityOfVectorNumber( uint32_t ulVectorNumber );
134 
135 /*-----------------------------------------------------------*/
136 
137 /* A variable is used to keep track of the critical section nesting.  This
138 variable must be initialised to a non zero value to ensure interrupts don't
139 inadvertently become unmasked before the scheduler starts. It is set to zero
140 before the first task starts executing. */
141 volatile uint32_t ulCriticalNesting = 9999UL;
142 
143 /* A structure used to map the various fields of an IDT entry into separate
144 structure members. */
145 struct IDTEntry
146 {
147     uint16_t usISRLow;              /* Low 16 bits of handler address. */
148     uint16_t usSegmentSelector;     /* Flat model means this is not changed. */
149     uint8_t ucZero;                 /* Must be set to zero. */
150     uint8_t ucFlags;                /* Flags for this entry. */
151     uint16_t usISRHigh;             /* High 16 bits of handler address. */
152 } __attribute__( ( packed ) );
153 typedef struct IDTEntry IDTEntry_t;
154 
155 
156 /* Use to pass the location of the IDT to the CPU. */
157 struct IDTPointer
158 {
159    uint16_t usTableLimit;
160    uint32_t ulTableBase;                /* The address of the first entry in xInterruptDescriptorTable. */
161 } __attribute__( ( __packed__ ) );
162 typedef struct IDTPointer IDTPointer_t;
163 
164 /* The IDT itself. */
165 static __attribute__ ( ( aligned( 32 ) ) ) IDTEntry_t xInterruptDescriptorTable[ portNUM_VECTORS ];
166 
167 #if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
168 
169     /* A table in which application defined interrupt handlers are stored.  These
170     are called by the central interrupt handler if a common interrupt entry
171     point it used. */
172     static ISR_Handler_t xInterruptHandlerTable[ portNUM_VECTORS ] = { NULL };
173 
174 #endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
175 
176 #if ( configSUPPORT_FPU == 1 )
177 
178     /* Saved as part of the task context.  If pucPortTaskFPUContextBuffer is NULL
179     then the task does not have an FPU context.  If pucPortTaskFPUContextBuffer is
180     not NULL then it points to a buffer into which the FPU context can be saved. */
181     uint8_t *pucPortTaskFPUContextBuffer __attribute__((used)) = pdFALSE;
182 
183 #endif /* configSUPPORT_FPU */
184 
185 /* The stack used by interrupt handlers. */
186 static uint32_t ulSystemStack[ configISR_STACK_SIZE ] __attribute__((used))  = { 0 };
187 
188 /* Don't use the very top of the system stack so the return address
189 appears as 0 if the debugger tries to unwind the stack. */
190 volatile uint32_t ulTopOfSystemStack __attribute__((used)) = ( uint32_t ) &( ulSystemStack[ configISR_STACK_SIZE - 5 ] );
191 
192 /* If a yield is requested from an interrupt or from a critical section then
193 the yield is not performed immediately, and ulPortYieldPending is set to pdTRUE
194 instead to indicate the yield should be performed at the end of the interrupt
195 when the critical section is exited. */
196 volatile uint32_t ulPortYieldPending __attribute__((used)) = pdFALSE;
197 
198 /* Counts the interrupt nesting depth.  Used to know when to switch to the
199 interrupt/system stack and when to save/restore a complete context. */
200 volatile uint32_t ulInterruptNesting __attribute__((used)) = 0;
201 
202 /*-----------------------------------------------------------*/
203 
204 /*
205  * See header file for description.
206  */
pxPortInitialiseStack(StackType_t * pxTopOfStack,TaskFunction_t pxCode,void * pvParameters)207 StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
208 {
209 uint32_t ulCodeSegment;
210 
211     /* Setup the initial stack as expected by the portFREERTOS_INTERRUPT_EXIT macro. */
212 
213     *pxTopOfStack = 0x00;
214     pxTopOfStack--;
215     *pxTopOfStack = 0x00;
216     pxTopOfStack--;
217 
218     /* Parameters first. */
219     *pxTopOfStack = ( StackType_t ) pvParameters;
220     pxTopOfStack--;
221 
222     /* There is nothing to return to so assert if attempting to use the return
223     address. */
224     *pxTopOfStack = ( StackType_t ) prvTaskExitError;
225     pxTopOfStack--;
226 
227     /* iret used to start the task pops up to here. */
228     *pxTopOfStack = portINITIAL_EFLAGS;
229     pxTopOfStack--;
230 
231     /* CS */
232     __asm volatile( "movl %%cs, %0" : "=r" ( ulCodeSegment ) );
233     *pxTopOfStack = ulCodeSegment;
234     pxTopOfStack--;
235 
236     /* First instruction in the task. */
237     *pxTopOfStack = ( StackType_t ) pxCode;
238     pxTopOfStack--;
239 
240     /* General purpose registers as expected by a POPA instruction. */
241     *pxTopOfStack = 0xEA;
242     pxTopOfStack--;
243 
244     *pxTopOfStack = 0xEC;
245     pxTopOfStack--;
246 
247     *pxTopOfStack = 0xED1; /* EDX */
248     pxTopOfStack--;
249 
250     *pxTopOfStack = 0xEB1; /* EBX */
251     pxTopOfStack--;
252 
253     /* Hole for ESP. */
254     pxTopOfStack--;
255 
256     *pxTopOfStack = 0x00; /* EBP */
257     pxTopOfStack--;
258 
259     *pxTopOfStack = 0xE5; /* ESI */
260     pxTopOfStack--;
261 
262     *pxTopOfStack = 0xeeeeeeee; /* EDI */
263 
264     #if ( configSUPPORT_FPU == 1 )
265     {
266         pxTopOfStack--;
267 
268         /* Buffer for FPU context, which is initialised to NULL as tasks are not
269         created with an FPU context. */
270         *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
271     }
272     #endif /* configSUPPORT_FPU */
273 
274     return pxTopOfStack;
275 }
276 /*-----------------------------------------------------------*/
277 
prvSetInterruptGate(uint8_t ucNumber,ISR_Handler_t pxHandlerFunction,uint8_t ucFlags)278 static void prvSetInterruptGate( uint8_t ucNumber, ISR_Handler_t pxHandlerFunction, uint8_t ucFlags )
279 {
280 uint16_t usCodeSegment;
281 uint32_t ulBase = ( uint32_t ) pxHandlerFunction;
282 
283     xInterruptDescriptorTable[ ucNumber ].usISRLow = ( uint16_t ) ( ulBase & USHRT_MAX );
284     xInterruptDescriptorTable[ ucNumber ].usISRHigh = ( uint16_t ) ( ( ulBase >> 16UL ) & USHRT_MAX );
285 
286     /* When the flat model is used the CS will never change. */
287     __asm volatile( "mov %%cs, %0" : "=r" ( usCodeSegment ) );
288     xInterruptDescriptorTable[ ucNumber ].usSegmentSelector = usCodeSegment;
289     xInterruptDescriptorTable[ ucNumber ].ucZero = 0;
290     xInterruptDescriptorTable[ ucNumber ].ucFlags = ucFlags;
291 }
292 /*-----------------------------------------------------------*/
293 
vPortSetupIDT(void)294 void vPortSetupIDT( void )
295 {
296 uint32_t ulNum;
297 IDTPointer_t xIDT;
298 
299     #if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
300     {
301         for( ulNum = 0; ulNum < portNUM_VECTORS; ulNum++ )
302         {
303             /* If a handler has not already been installed on this vector. */
304             if( ( xInterruptDescriptorTable[ ulNum ].usISRLow == 0x00 ) && ( xInterruptDescriptorTable[ ulNum ].usISRHigh == 0x00 ) )
305             {
306                 prvSetInterruptGate( ( uint8_t ) ulNum, vPortCentralInterruptWrapper, portIDT_FLAGS );
307             }
308         }
309     }
310     #endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
311 
312     /* Set IDT address. */
313     xIDT.ulTableBase = ( uint32_t ) xInterruptDescriptorTable;
314     xIDT.usTableLimit = sizeof( xInterruptDescriptorTable ) - 1;
315 
316     /* Set IDT in CPU. */
317     __asm volatile( "lidt %0" :: "m" (xIDT) );
318 }
319 /*-----------------------------------------------------------*/
320 
prvTaskExitError(void)321 static void prvTaskExitError( void )
322 {
323     /* A function that implements a task must not exit or attempt to return to
324     its caller as there is nothing to return to.  If a task wants to exit it
325     should instead call vTaskDelete( NULL ).
326 
327     Artificially force an assert() to be triggered if configASSERT() is
328     defined, then stop here so application writers can catch the error. */
329     configASSERT( ulCriticalNesting == ~0UL );
330     portDISABLE_INTERRUPTS();
331     for( ;; );
332 }
333 /*-----------------------------------------------------------*/
334 
prvSetupTimerInterrupt(void)335 static void prvSetupTimerInterrupt( void )
336 {
337 extern void vPortAPICErrorHandlerWrapper( void );
338 extern void vPortAPICSpuriousHandler( void );
339 
340     /* Initialise LAPIC to a well known state. */
341     portAPIC_LDR = 0xFFFFFFFF;
342     portAPIC_LDR = ( ( portAPIC_LDR & 0x00FFFFFF ) | 0x00000001 );
343     portAPIC_LVT_TIMER = portAPIC_DISABLE;
344     portAPIC_LVT_PERF = portAPIC_NMI;
345     portAPIC_LVT_LINT0 = portAPIC_DISABLE;
346     portAPIC_LVT_LINT1 = portAPIC_DISABLE;
347     portAPIC_TASK_PRIORITY = 0;
348 
349     /* Install APIC timer ISR vector. */
350     prvSetInterruptGate( ( uint8_t ) portAPIC_TIMER_INT_VECTOR, vPortTimerHandler, portIDT_FLAGS );
351 
352     /* Install API error handler. */
353     prvSetInterruptGate( ( uint8_t ) portAPIC_LVT_ERROR_VECTOR, vPortAPICErrorHandlerWrapper, portIDT_FLAGS );
354 
355     /* Install Yield handler. */
356     prvSetInterruptGate( ( uint8_t ) portAPIC_YIELD_INT_VECTOR, vPortYieldCall, portIDT_FLAGS );
357 
358     /* Install spurious interrupt vector. */
359     prvSetInterruptGate( ( uint8_t ) portAPIC_SPURIOUS_INT_VECTOR, vPortAPICSpuriousHandler, portIDT_FLAGS );
360 
361     /* Enable the APIC, mapping the spurious interrupt at the same time. */
362     portAPIC_SPURIOUS_INT = portAPIC_SPURIOUS_INT_VECTOR | portAPIC_ENABLE_BIT;
363 
364     /* Set timer error vector. */
365     portAPIC_LVT_ERROR = portAPIC_LVT_ERROR_VECTOR;
366 
367     /* Set the interrupt frequency. */
368     portAPIC_TMRDIV = portAPIC_DIV_16;
369     portAPIC_TIMER_INITIAL_COUNT = ( ( configCPU_CLOCK_HZ >> 4UL ) / configTICK_RATE_HZ ) - 1UL;
370 }
371 /*-----------------------------------------------------------*/
372 
xPortStartScheduler(void)373 BaseType_t xPortStartScheduler( void )
374 {
375 BaseType_t xWord;
376 
377     /* Some versions of GCC require the -mno-ms-bitfields command line option
378     for packing to work. */
379     configASSERT( sizeof( struct IDTEntry ) == portEXPECTED_IDT_ENTRY_SIZE );
380 
381     /* Fill part of the system stack with a known value to help detect stack
382     overflow.  A few zeros are left so GDB doesn't get confused unwinding
383     the stack. */
384     for( xWord = 0; xWord < configISR_STACK_SIZE - 20; xWord++ )
385     {
386         ulSystemStack[ xWord ] = portSTACK_WORD;
387     }
388 
389     /* Initialise Interrupt Descriptor Table (IDT). */
390     vPortSetupIDT();
391 
392     /* Initialise LAPIC and install system handlers. */
393     prvSetupTimerInterrupt();
394 
395     /* Make sure the stack used by interrupts is aligned. */
396     ulTopOfSystemStack &= ~portBYTE_ALIGNMENT_MASK;
397 
398     ulCriticalNesting = 0;
399 
400     /* Enable LAPIC Counter.*/
401     portAPIC_LVT_TIMER = portAPIC_TIMER_PERIODIC | portAPIC_TIMER_INT_VECTOR;
402 
403     /* Sometimes needed. */
404     portAPIC_TMRDIV = portAPIC_DIV_16;
405 
406     /* Should not return from the following function as the scheduler will then
407     be executing the tasks. */
408     vPortStartFirstTask();
409 
410     return 0;
411 }
412 /*-----------------------------------------------------------*/
413 
vPortEndScheduler(void)414 void vPortEndScheduler( void )
415 {
416     /* Not implemented in ports where there is nothing to return to.
417     Artificially force an assert. */
418     configASSERT( ulCriticalNesting == 1000UL );
419 }
420 /*-----------------------------------------------------------*/
421 
vPortEnterCritical(void)422 void vPortEnterCritical( void )
423 {
424     if( ulCriticalNesting == 0 )
425     {
426         #if( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
427         {
428             __asm volatile( "cli" );
429         }
430         #else
431         {
432             portAPIC_TASK_PRIORITY = portMAX_API_CALL_PRIORITY;
433             configASSERT( portAPIC_TASK_PRIORITY == portMAX_API_CALL_PRIORITY );
434         }
435         #endif
436     }
437 
438     /* Now interrupts are disabled ulCriticalNesting can be accessed
439     directly.  Increment ulCriticalNesting to keep a count of how many times
440     portENTER_CRITICAL() has been called. */
441     ulCriticalNesting++;
442 }
443 /*-----------------------------------------------------------*/
444 
vPortExitCritical(void)445 void vPortExitCritical( void )
446 {
447     if( ulCriticalNesting > portNO_CRITICAL_NESTING )
448     {
449         /* Decrement the nesting count as the critical section is being
450         exited. */
451         ulCriticalNesting--;
452 
453         /* If the nesting level has reached zero then all interrupt
454         priorities must be re-enabled. */
455         if( ulCriticalNesting == portNO_CRITICAL_NESTING )
456         {
457             /* Critical nesting has reached zero so all interrupt priorities
458             should be unmasked. */
459             #if( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
460             {
461                 __asm volatile( "sti" );
462             }
463             #else
464             {
465                 portAPIC_TASK_PRIORITY = 0;
466             }
467             #endif
468 
469             /* If a yield was pended from within the critical section then
470             perform the yield now. */
471             if( ulPortYieldPending != pdFALSE )
472             {
473                 ulPortYieldPending = pdFALSE;
474                 __asm volatile( portYIELD_INTERRUPT );
475             }
476         }
477     }
478 }
479 /*-----------------------------------------------------------*/
480 
ulPortSetInterruptMask(void)481 uint32_t ulPortSetInterruptMask( void )
482 {
483 volatile uint32_t ulOriginalMask;
484 
485     /* Set mask to max syscall priority. */
486     #if( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
487     {
488         /* Return whether interrupts were already enabled or not.  Pop adjusts
489         the stack first. */
490         __asm volatile( "pushf      \t\n"
491                         "pop %0     \t\n"
492                         "cli            "
493                         : "=rm" (ulOriginalMask) :: "memory" );
494 
495         ulOriginalMask &= portEFLAGS_IF;
496     }
497     #else
498     {
499         /* Return original mask. */
500         ulOriginalMask = portAPIC_TASK_PRIORITY;
501         portAPIC_TASK_PRIORITY = portMAX_API_CALL_PRIORITY;
502         configASSERT( portAPIC_TASK_PRIORITY == portMAX_API_CALL_PRIORITY );
503     }
504     #endif
505 
506     return ulOriginalMask;
507 }
508 /*-----------------------------------------------------------*/
509 
vPortClearInterruptMask(uint32_t ulNewMaskValue)510 void vPortClearInterruptMask( uint32_t ulNewMaskValue )
511 {
512     #if( configMAX_API_CALL_INTERRUPT_PRIORITY == portMAX_PRIORITY )
513     {
514         if( ulNewMaskValue != pdFALSE )
515         {
516             __asm volatile( "sti" );
517         }
518     }
519     #else
520     {
521         portAPIC_TASK_PRIORITY = ulNewMaskValue;
522         configASSERT( portAPIC_TASK_PRIORITY == ulNewMaskValue );
523     }
524     #endif
525 }
526 /*-----------------------------------------------------------*/
527 
528 #if ( configSUPPORT_FPU == 1 )
529 
vPortTaskUsesFPU(void)530     void vPortTaskUsesFPU( void )
531     {
532         /* A task is registering the fact that it needs an FPU context.  Allocate a
533         buffer into which the context can be saved. */
534         pucPortTaskFPUContextBuffer = ( uint8_t * ) pvPortMalloc( portFPU_CONTEXT_SIZE_BYTES );
535         configASSERT( pucPortTaskFPUContextBuffer );
536 
537         /* Initialise the floating point registers. */
538         __asm volatile( "fninit" );
539     }
540 
541 #endif /* configSUPPORT_FPU */
542 /*-----------------------------------------------------------*/
543 
vPortAPICErrorHandler(void)544 void vPortAPICErrorHandler( void )
545 {
546 /* Variable to hold the APIC error status for viewing in the debugger. */
547 volatile uint32_t ulErrorStatus = 0;
548 
549     portAPIC_ERROR_STATUS = 0;
550     ulErrorStatus = portAPIC_ERROR_STATUS;
551     ( void ) ulErrorStatus;
552 
553     /* Force an assert. */
554     configASSERT( ulCriticalNesting == ~0UL );
555 }
556 /*-----------------------------------------------------------*/
557 
558 #if( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
559 
vPortCentralInterruptHandler(uint32_t ulVector)560     void vPortCentralInterruptHandler( uint32_t ulVector )
561     {
562         if( ulVector < portNUM_VECTORS )
563         {
564             if( xInterruptHandlerTable[ ulVector ] != NULL )
565             {
566                 ( xInterruptHandlerTable[ ulVector ] )();
567             }
568         }
569 
570         /* Check for a system stack overflow. */
571         configASSERT( ulSystemStack[ 10 ] == portSTACK_WORD );
572         configASSERT( ulSystemStack[ 12 ] == portSTACK_WORD );
573         configASSERT( ulSystemStack[ 14 ] == portSTACK_WORD );
574     }
575 
576 #endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
577 /*-----------------------------------------------------------*/
578 
579 #if ( configUSE_COMMON_INTERRUPT_ENTRY_POINT == 1 )
580 
xPortRegisterCInterruptHandler(ISR_Handler_t pxHandler,uint32_t ulVectorNumber)581     BaseType_t xPortRegisterCInterruptHandler( ISR_Handler_t pxHandler, uint32_t ulVectorNumber )
582     {
583     BaseType_t xReturn;
584 
585         xReturn = prvCheckValidityOfVectorNumber( ulVectorNumber );
586 
587         if( xReturn != pdFAIL )
588         {
589             /* Save the handler passed in by the application in the vector number
590             passed in.  The addresses are then called from the central interrupt
591             handler. */
592             xInterruptHandlerTable[ ulVectorNumber ] = pxHandler;
593         }
594 
595         return xReturn;
596     }
597 
598 #endif /* configUSE_COMMON_INTERRUPT_ENTRY_POINT */
599 /*-----------------------------------------------------------*/
600 
xPortInstallInterruptHandler(ISR_Handler_t pxHandler,uint32_t ulVectorNumber)601 BaseType_t xPortInstallInterruptHandler( ISR_Handler_t pxHandler, uint32_t ulVectorNumber )
602 {
603 BaseType_t xReturn;
604 
605     xReturn = prvCheckValidityOfVectorNumber( ulVectorNumber );
606 
607     if( xReturn != pdFAIL )
608     {
609         taskENTER_CRITICAL();
610         {
611             /* Update the IDT to include the application defined handler. */
612             prvSetInterruptGate( ( uint8_t ) ulVectorNumber, ( ISR_Handler_t ) pxHandler, portIDT_FLAGS );
613         }
614         taskEXIT_CRITICAL();
615     }
616 
617     return xReturn;
618 }
619 /*-----------------------------------------------------------*/
620 
prvCheckValidityOfVectorNumber(uint32_t ulVectorNumber)621 static BaseType_t prvCheckValidityOfVectorNumber( uint32_t ulVectorNumber )
622 {
623 BaseType_t xReturn;
624 
625     /* Check validity of vector number. */
626     if( ulVectorNumber >= portNUM_VECTORS )
627     {
628         /* Too high. */
629         xReturn = pdFAIL;
630     }
631     else if( ulVectorNumber < portAPIC_MIN_ALLOWABLE_VECTOR )
632     {
633         /* Too low. */
634         xReturn = pdFAIL;
635     }
636     else if( ulVectorNumber == portAPIC_TIMER_INT_VECTOR )
637     {
638         /* In use by FreeRTOS. */
639         xReturn = pdFAIL;
640     }
641     else if( ulVectorNumber == portAPIC_YIELD_INT_VECTOR )
642     {
643         /* In use by FreeRTOS. */
644         xReturn = pdFAIL;
645     }
646     else if( ulVectorNumber == portAPIC_LVT_ERROR_VECTOR )
647     {
648         /* In use by FreeRTOS. */
649         xReturn = pdFAIL;
650     }
651     else if( ulVectorNumber == portAPIC_SPURIOUS_INT_VECTOR )
652     {
653         /* In use by FreeRTOS. */
654         xReturn = pdFAIL;
655     }
656     else if( xInterruptHandlerTable[ ulVectorNumber ] != NULL )
657     {
658         /* Already in use by the application. */
659         xReturn = pdFAIL;
660     }
661     else
662     {
663         xReturn = pdPASS;
664     }
665 
666     return xReturn;
667 }
668 /*-----------------------------------------------------------*/
669 
vGenerateYieldInterrupt(void)670 void vGenerateYieldInterrupt( void )
671 {
672     __asm volatile( portYIELD_INTERRUPT );
673 }
674