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