]> begriffs open source - freertos/blob - portable/IAR/ARM_CM55/secure/secure_heap.c
[AUTO][RELEASE]: Bump file header version to "10.5.0"
[freertos] / portable / IAR / ARM_CM55 / secure / secure_heap.c
1 /*
2  * FreeRTOS Kernel V10.5.0
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 <stdint.h>
31
32 /* Secure context heap includes. */
33 #include "secure_heap.h"
34
35 /* Secure port macros. */
36 #include "secure_port_macros.h"
37
38 /**
39  * @brief Total heap size.
40  */
41 #ifndef secureconfigTOTAL_HEAP_SIZE
42     #define secureconfigTOTAL_HEAP_SIZE    ( ( ( size_t ) ( 10 * 1024 ) ) )
43 #endif
44
45 /* No test marker by default. */
46 #ifndef mtCOVERAGE_TEST_MARKER
47     #define mtCOVERAGE_TEST_MARKER()
48 #endif
49
50 /* No tracing by default. */
51 #ifndef traceMALLOC
52     #define traceMALLOC( pvReturn, xWantedSize )
53 #endif
54
55 /* No tracing by default. */
56 #ifndef traceFREE
57     #define traceFREE( pv, xBlockSize )
58 #endif
59
60 /* Block sizes must not get too small. */
61 #define secureheapMINIMUM_BLOCK_SIZE    ( ( size_t ) ( xHeapStructSize << 1 ) )
62
63 /* Assumes 8bit bytes! */
64 #define secureheapBITS_PER_BYTE         ( ( size_t ) 8 )
65 /*-----------------------------------------------------------*/
66
67 /* Allocate the memory for the heap. */
68 #if ( configAPPLICATION_ALLOCATED_HEAP == 1 )
69
70 /* The application writer has already defined the array used for the RTOS
71 * heap - probably so it can be placed in a special segment or address. */
72     extern uint8_t ucHeap[ secureconfigTOTAL_HEAP_SIZE ];
73 #else /* configAPPLICATION_ALLOCATED_HEAP */
74     static uint8_t ucHeap[ secureconfigTOTAL_HEAP_SIZE ];
75 #endif /* configAPPLICATION_ALLOCATED_HEAP */
76
77 /**
78  * @brief The linked list structure.
79  *
80  * This is used to link free blocks in order of their memory address.
81  */
82 typedef struct A_BLOCK_LINK
83 {
84     struct A_BLOCK_LINK * pxNextFreeBlock; /**< The next free block in the list. */
85     size_t xBlockSize;                     /**< The size of the free block. */
86 } BlockLink_t;
87 /*-----------------------------------------------------------*/
88
89 /**
90  * @brief Called automatically to setup the required heap structures the first
91  * time pvPortMalloc() is called.
92  */
93 static void prvHeapInit( void );
94
95 /**
96  * @brief Inserts a block of memory that is being freed into the correct
97  * position in the list of free memory blocks.
98  *
99  * The block being freed will be merged with the block in front it and/or the
100  * block behind it if the memory blocks are adjacent to each other.
101  *
102  * @param[in] pxBlockToInsert The block being freed.
103  */
104 static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert );
105 /*-----------------------------------------------------------*/
106
107 /**
108  * @brief The size of the structure placed at the beginning of each allocated
109  * memory block must by correctly byte aligned.
110  */
111 static const size_t xHeapStructSize = ( sizeof( BlockLink_t ) + ( ( size_t ) ( secureportBYTE_ALIGNMENT - 1 ) ) ) & ~( ( size_t ) secureportBYTE_ALIGNMENT_MASK );
112
113 /**
114  * @brief Create a couple of list links to mark the start and end of the list.
115  */
116 static BlockLink_t xStart, * pxEnd = NULL;
117
118 /**
119  * @brief Keeps track of the number of free bytes remaining, but says nothing
120  * about fragmentation.
121  */
122 static size_t xFreeBytesRemaining = 0U;
123 static size_t xMinimumEverFreeBytesRemaining = 0U;
124
125 /**
126  * @brief Gets set to the top bit of an size_t type.
127  *
128  * When this bit in the xBlockSize member of an BlockLink_t structure is set
129  * then the block belongs to the application. When the bit is free the block is
130  * still part of the free heap space.
131  */
132 static size_t xBlockAllocatedBit = 0;
133 /*-----------------------------------------------------------*/
134
135 static void prvHeapInit( void )
136 {
137     BlockLink_t * pxFirstFreeBlock;
138     uint8_t * pucAlignedHeap;
139     size_t uxAddress;
140     size_t xTotalHeapSize = secureconfigTOTAL_HEAP_SIZE;
141
142     /* Ensure the heap starts on a correctly aligned boundary. */
143     uxAddress = ( size_t ) ucHeap;
144
145     if( ( uxAddress & secureportBYTE_ALIGNMENT_MASK ) != 0 )
146     {
147         uxAddress += ( secureportBYTE_ALIGNMENT - 1 );
148         uxAddress &= ~( ( size_t ) secureportBYTE_ALIGNMENT_MASK );
149         xTotalHeapSize -= uxAddress - ( size_t ) ucHeap;
150     }
151
152     pucAlignedHeap = ( uint8_t * ) uxAddress;
153
154     /* xStart is used to hold a pointer to the first item in the list of free
155      * blocks.  The void cast is used to prevent compiler warnings. */
156     xStart.pxNextFreeBlock = ( void * ) pucAlignedHeap;
157     xStart.xBlockSize = ( size_t ) 0;
158
159     /* pxEnd is used to mark the end of the list of free blocks and is inserted
160      * at the end of the heap space. */
161     uxAddress = ( ( size_t ) pucAlignedHeap ) + xTotalHeapSize;
162     uxAddress -= xHeapStructSize;
163     uxAddress &= ~( ( size_t ) secureportBYTE_ALIGNMENT_MASK );
164     pxEnd = ( void * ) uxAddress;
165     pxEnd->xBlockSize = 0;
166     pxEnd->pxNextFreeBlock = NULL;
167
168     /* To start with there is a single free block that is sized to take up the
169      * entire heap space, minus the space taken by pxEnd. */
170     pxFirstFreeBlock = ( void * ) pucAlignedHeap;
171     pxFirstFreeBlock->xBlockSize = uxAddress - ( size_t ) pxFirstFreeBlock;
172     pxFirstFreeBlock->pxNextFreeBlock = pxEnd;
173
174     /* Only one block exists - and it covers the entire usable heap space. */
175     xMinimumEverFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
176     xFreeBytesRemaining = pxFirstFreeBlock->xBlockSize;
177
178     /* Work out the position of the top bit in a size_t variable. */
179     xBlockAllocatedBit = ( ( size_t ) 1 ) << ( ( sizeof( size_t ) * secureheapBITS_PER_BYTE ) - 1 );
180 }
181 /*-----------------------------------------------------------*/
182
183 static void prvInsertBlockIntoFreeList( BlockLink_t * pxBlockToInsert )
184 {
185     BlockLink_t * pxIterator;
186     uint8_t * puc;
187
188     /* Iterate through the list until a block is found that has a higher address
189      * than the block being inserted. */
190     for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock )
191     {
192         /* Nothing to do here, just iterate to the right position. */
193     }
194
195     /* Do the block being inserted, and the block it is being inserted after
196      * make a contiguous block of memory? */
197     puc = ( uint8_t * ) pxIterator;
198
199     if( ( puc + pxIterator->xBlockSize ) == ( uint8_t * ) pxBlockToInsert )
200     {
201         pxIterator->xBlockSize += pxBlockToInsert->xBlockSize;
202         pxBlockToInsert = pxIterator;
203     }
204     else
205     {
206         mtCOVERAGE_TEST_MARKER();
207     }
208
209     /* Do the block being inserted, and the block it is being inserted before
210      * make a contiguous block of memory? */
211     puc = ( uint8_t * ) pxBlockToInsert;
212
213     if( ( puc + pxBlockToInsert->xBlockSize ) == ( uint8_t * ) pxIterator->pxNextFreeBlock )
214     {
215         if( pxIterator->pxNextFreeBlock != pxEnd )
216         {
217             /* Form one big block from the two blocks. */
218             pxBlockToInsert->xBlockSize += pxIterator->pxNextFreeBlock->xBlockSize;
219             pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock->pxNextFreeBlock;
220         }
221         else
222         {
223             pxBlockToInsert->pxNextFreeBlock = pxEnd;
224         }
225     }
226     else
227     {
228         pxBlockToInsert->pxNextFreeBlock = pxIterator->pxNextFreeBlock;
229     }
230
231     /* If the block being inserted plugged a gab, so was merged with the block
232      * before and the block after, then it's pxNextFreeBlock pointer will have
233      * already been set, and should not be set here as that would make it point
234      * to itself. */
235     if( pxIterator != pxBlockToInsert )
236     {
237         pxIterator->pxNextFreeBlock = pxBlockToInsert;
238     }
239     else
240     {
241         mtCOVERAGE_TEST_MARKER();
242     }
243 }
244 /*-----------------------------------------------------------*/
245
246 void * pvPortMalloc( size_t xWantedSize )
247 {
248     BlockLink_t * pxBlock, * pxPreviousBlock, * pxNewBlockLink;
249     void * pvReturn = NULL;
250
251     /* If this is the first call to malloc then the heap will require
252      * initialisation to setup the list of free blocks. */
253     if( pxEnd == NULL )
254     {
255         prvHeapInit();
256     }
257     else
258     {
259         mtCOVERAGE_TEST_MARKER();
260     }
261
262     /* Check the requested block size is not so large that the top bit is set.
263      * The top bit of the block size member of the BlockLink_t structure is used
264      * to determine who owns the block - the application or the kernel, so it
265      * must be free. */
266     if( ( xWantedSize & xBlockAllocatedBit ) == 0 )
267     {
268         /* The wanted size is increased so it can contain a BlockLink_t
269          * structure in addition to the requested amount of bytes. */
270         if( xWantedSize > 0 )
271         {
272             xWantedSize += xHeapStructSize;
273
274             /* Ensure that blocks are always aligned to the required number of
275              * bytes. */
276             if( ( xWantedSize & secureportBYTE_ALIGNMENT_MASK ) != 0x00 )
277             {
278                 /* Byte alignment required. */
279                 xWantedSize += ( secureportBYTE_ALIGNMENT - ( xWantedSize & secureportBYTE_ALIGNMENT_MASK ) );
280                 secureportASSERT( ( xWantedSize & secureportBYTE_ALIGNMENT_MASK ) == 0 );
281             }
282             else
283             {
284                 mtCOVERAGE_TEST_MARKER();
285             }
286         }
287         else
288         {
289             mtCOVERAGE_TEST_MARKER();
290         }
291
292         if( ( xWantedSize > 0 ) && ( xWantedSize <= xFreeBytesRemaining ) )
293         {
294             /* Traverse the list from the start (lowest address) block until
295              * one of adequate size is found. */
296             pxPreviousBlock = &xStart;
297             pxBlock = xStart.pxNextFreeBlock;
298
299             while( ( pxBlock->xBlockSize < xWantedSize ) && ( pxBlock->pxNextFreeBlock != NULL ) )
300             {
301                 pxPreviousBlock = pxBlock;
302                 pxBlock = pxBlock->pxNextFreeBlock;
303             }
304
305             /* If the end marker was reached then a block of adequate size was
306              * not found. */
307             if( pxBlock != pxEnd )
308             {
309                 /* Return the memory space pointed to - jumping over the
310                  * BlockLink_t structure at its start. */
311                 pvReturn = ( void * ) ( ( ( uint8_t * ) pxPreviousBlock->pxNextFreeBlock ) + xHeapStructSize );
312
313                 /* This block is being returned for use so must be taken out
314                  * of the list of free blocks. */
315                 pxPreviousBlock->pxNextFreeBlock = pxBlock->pxNextFreeBlock;
316
317                 /* If the block is larger than required it can be split into
318                  * two. */
319                 if( ( pxBlock->xBlockSize - xWantedSize ) > secureheapMINIMUM_BLOCK_SIZE )
320                 {
321                     /* This block is to be split into two.  Create a new
322                      * block following the number of bytes requested. The void
323                      * cast is used to prevent byte alignment warnings from the
324                      * compiler. */
325                     pxNewBlockLink = ( void * ) ( ( ( uint8_t * ) pxBlock ) + xWantedSize );
326                     secureportASSERT( ( ( ( size_t ) pxNewBlockLink ) & secureportBYTE_ALIGNMENT_MASK ) == 0 );
327
328                     /* Calculate the sizes of two blocks split from the single
329                      * block. */
330                     pxNewBlockLink->xBlockSize = pxBlock->xBlockSize - xWantedSize;
331                     pxBlock->xBlockSize = xWantedSize;
332
333                     /* Insert the new block into the list of free blocks. */
334                     prvInsertBlockIntoFreeList( pxNewBlockLink );
335                 }
336                 else
337                 {
338                     mtCOVERAGE_TEST_MARKER();
339                 }
340
341                 xFreeBytesRemaining -= pxBlock->xBlockSize;
342
343                 if( xFreeBytesRemaining < xMinimumEverFreeBytesRemaining )
344                 {
345                     xMinimumEverFreeBytesRemaining = xFreeBytesRemaining;
346                 }
347                 else
348                 {
349                     mtCOVERAGE_TEST_MARKER();
350                 }
351
352                 /* The block is being returned - it is allocated and owned by
353                  * the application and has no "next" block. */
354                 pxBlock->xBlockSize |= xBlockAllocatedBit;
355                 pxBlock->pxNextFreeBlock = NULL;
356             }
357             else
358             {
359                 mtCOVERAGE_TEST_MARKER();
360             }
361         }
362         else
363         {
364             mtCOVERAGE_TEST_MARKER();
365         }
366     }
367     else
368     {
369         mtCOVERAGE_TEST_MARKER();
370     }
371
372     traceMALLOC( pvReturn, xWantedSize );
373
374     #if ( secureconfigUSE_MALLOC_FAILED_HOOK == 1 )
375         {
376             if( pvReturn == NULL )
377             {
378                 extern void vApplicationMallocFailedHook( void );
379                 vApplicationMallocFailedHook();
380             }
381             else
382             {
383                 mtCOVERAGE_TEST_MARKER();
384             }
385         }
386     #endif /* if ( secureconfigUSE_MALLOC_FAILED_HOOK == 1 ) */
387
388     secureportASSERT( ( ( ( size_t ) pvReturn ) & ( size_t ) secureportBYTE_ALIGNMENT_MASK ) == 0 );
389     return pvReturn;
390 }
391 /*-----------------------------------------------------------*/
392
393 void vPortFree( void * pv )
394 {
395     uint8_t * puc = ( uint8_t * ) pv;
396     BlockLink_t * pxLink;
397
398     if( pv != NULL )
399     {
400         /* The memory being freed will have an BlockLink_t structure immediately
401          * before it. */
402         puc -= xHeapStructSize;
403
404         /* This casting is to keep the compiler from issuing warnings. */
405         pxLink = ( void * ) puc;
406
407         /* Check the block is actually allocated. */
408         secureportASSERT( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 );
409         secureportASSERT( pxLink->pxNextFreeBlock == NULL );
410
411         if( ( pxLink->xBlockSize & xBlockAllocatedBit ) != 0 )
412         {
413             if( pxLink->pxNextFreeBlock == NULL )
414             {
415                 /* The block is being returned to the heap - it is no longer
416                  * allocated. */
417                 pxLink->xBlockSize &= ~xBlockAllocatedBit;
418
419                 secureportDISABLE_NON_SECURE_INTERRUPTS();
420                 {
421                     /* Add this block to the list of free blocks. */
422                     xFreeBytesRemaining += pxLink->xBlockSize;
423                     traceFREE( pv, pxLink->xBlockSize );
424                     prvInsertBlockIntoFreeList( ( ( BlockLink_t * ) pxLink ) );
425                 }
426                 secureportENABLE_NON_SECURE_INTERRUPTS();
427             }
428             else
429             {
430                 mtCOVERAGE_TEST_MARKER();
431             }
432         }
433         else
434         {
435             mtCOVERAGE_TEST_MARKER();
436         }
437     }
438 }
439 /*-----------------------------------------------------------*/
440
441 size_t xPortGetFreeHeapSize( void )
442 {
443     return xFreeBytesRemaining;
444 }
445 /*-----------------------------------------------------------*/
446
447 size_t xPortGetMinimumEverFreeHeapSize( void )
448 {
449     return xMinimumEverFreeBytesRemaining;
450 }
451 /*-----------------------------------------------------------*/