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