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