-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathheap_useNewlib_NXP.c
240 lines (223 loc) · 11.2 KB
/
heap_useNewlib_NXP.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
/**
* \file heap_useNewlib_NXP.c
* \brief Wrappers required to use newlib malloc-family within FreeRTOS.
*
* \par Overview
* Route FreeRTOS memory management functions to newlib's malloc family.
* Thus newlib and FreeRTOS share memory-management routines and memory pool,
* and all newlib's internal memory-management requirements are supported.
*
* \author Dave Nadler
* \date 22-July-2017
* \version 3-Jan-2023 Correct _malloc_r signature+call for malloc wrap
* \version 3-Jan-2023 Function declarations and unused arguments for picky compiler
* \version 27-Jun-2020 Correct "FreeRTOS.h" capitalization, commentary
* \version 24-Jun-2020 commentary only
* \version 11-Sep-2019 malloc accounting, comments, newlib version check
*
* \see http://www.nadler.com/embedded/newlibAndFreeRTOS.html
* \see https://sourceware.org/newlib/libc.html#Reentrancy
* \see https://sourceware.org/newlib/libc.html#malloc
* \see https://sourceware.org/newlib/libc.html#index-_005f_005fenv_005flock
* \see https://sourceware.org/newlib/libc.html#index-_005f_005fmalloc_005flock
* \see https://sourceforge.net/p/freertos/feature-requests/72/
* \see http://www.billgatliff.com/newlib.html
* \see http://wiki.osdev.org/Porting_Newlib
* \see http://www.embecosm.com/appnotes/ean9/ean9-howto-newlib-1.0.html
*
*
* \copyright
* (c) Dave Nadler 2017-2020, All Rights Reserved.
* Web: http://www.nadler.com
* email: [email protected]
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* - Use or redistributions of source code must retain the above copyright notice,
* this list of conditions, and the following disclaimer.
*
* - Use or redistributions of source code must retain ALL ORIGINAL COMMENTS, AND
* ANY CHANGES MUST BE DOCUMENTED, INCLUDING:
* - Reason for change (purpose)
* - Functional change
* - Date and author contact
*
* - Redistributions in binary form must reproduce the above copyright notice, this
* list of conditions and the following disclaimer in the documentation and/or
* other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h> // maps to newlib...
#include <malloc.h> // mallinfo...
#include <errno.h> // ENOMEM
#include <stdbool.h>
#include <stddef.h>
#include "newlib.h"
#if ((__NEWLIB__ == 2) && (__NEWLIB_MINOR__ < 5)) || ((__NEWLIB__ == 4) && (__NEWLIB_MINOR__ > 2) || (__NEWLIB__ < 2) || (__NEWLIB__ > 4))
#warning "This wrapper was verified for newlib versions 2.5 - 4.2; please ensure newlib's external requirements for malloc-family are unchanged!"
#endif
#include "FreeRTOS.h" // defines public interface we're implementing here
#if !defined(configUSE_NEWLIB_REENTRANT) || (configUSE_NEWLIB_REENTRANT!=1)
#warning "#define configUSE_NEWLIB_REENTRANT 1 // Required for thread-safety of newlib sprintf, dtoa, strtok, etc..."
// If you're *REALLY* sure you don't need FreeRTOS's newlib reentrancy support, comment out the above warning...
#endif
#include "task.h"
// ================================================================================================
// External routines required by newlib's malloc (sbrk/_sbrk, __malloc_lock/unlock)
// ================================================================================================
// Simplistic sbrk implementations assume stack grows downwards from top of memory,
// and heap grows upwards starting just after BSS.
// FreeRTOS normally allocates task stacks from a pool placed within BSS or DATA.
// Thus within a FreeRTOS task, stack pointer is always below end of BSS.
// When using this module, stacks are allocated from malloc pool, still always prior
// current unused heap area...
// Doesn't work with FreeRTOS: suggested minimal implementation from https://sourceware.org/newlib/libc.html#Syscalls:
#if 0
// sbrk: Increase program data space. As malloc and related functions depend on this,
// it is useful to have a working implementation. The following suffices for a standalone system;
// it exploits the symbol _end automatically defined by the GNU linker.
caddr_t sbrk(int incr) {
extern char _end; /* Defined by the linker */
static char *heap_end;
char *prev_heap_end;
if (heap_end == 0) {
heap_end = &_end;
}
prev_heap_end = heap_end;
if (heap_end + incr > stack_ptr) { // Fails here: always true for FreeRTOS task stacks
write (1, "Heap and stack collision\n", 25);
abort ();
}
heap_end += incr;
return (caddr_t) prev_heap_end;
}
#endif
// Doesn't work with FreeRTOS: Freescale implementation
#if 0
caddr_t _sbrk(int incr)
{
extern char end __asm("end");
extern char heap_limit __asm("__HeapLimit");
static char *heap_end;
char *prev_heap_end;
if (heap_end == NULL)
heap_end = &end;
prev_heap_end = heap_end;
if (heap_end + incr > &heap_limit) // Fails here: always true for FreeRTOS task stacks
{
errno = ENOMEM; // ...so first call inside a FreeRTOS task lands here
return (caddr_t)-1;
}
heap_end += incr;
return (caddr_t)prev_heap_end;
}
#endif
#ifndef NDEBUG
static int totalBytesProvidedBySBRK = 0;
#endif
extern char __HeapBase, __HeapLimit, HEAP_SIZE; // make sure to define these symbols in linker command file
static int heapBytesRemaining = (int)&HEAP_SIZE; // that's (&__HeapLimit)-(&__HeapBase)
// Use of vTaskSuspendAll() in _sbrk_r() is normally redundant, as newlib malloc family routines call
// __malloc_lock before calling _sbrk_r(). Note vTaskSuspendAll/xTaskResumeAll support nesting.
//! _sbrk_r version supporting reentrant newlib (depends upon above symbols defined by linker control file).
void * _sbrk_r(struct _reent *pReent, int incr) {
(void)pReent;
static char *currentHeapEnd = &__HeapBase;
vTaskSuspendAll(); // Note: safe to use before FreeRTOS scheduler started, but not within an ISR
if (currentHeapEnd + incr > &__HeapLimit) {
// Ooops, no more memory available...
#if( configUSE_MALLOC_FAILED_HOOK == 1 )
{
extern void vApplicationMallocFailedHook( void );
vApplicationMallocFailedHook();
}
#elif defined(configHARD_STOP_ON_MALLOC_FAILURE)
// If you want to alert debugger or halt...
while(1) { __asm("bkpt #0"); } // Stop in GUI as if at a breakpoint (if debugging, otherwise loop forever)
#else
// Default, if you prefer to believe your application will gracefully trap out-of-memory...
pReent->_errno = ENOMEM; // newlib's thread-specific errno
xTaskResumeAll(); // Note: safe to use before FreeRTOS scheduler started, but not within an ISR;
#endif
return (char *)-1; // the malloc-family routine that called sbrk will return 0
}
// 'incr' of memory is available: update accounting and return it.
char *previousHeapEnd = currentHeapEnd;
currentHeapEnd += incr;
heapBytesRemaining -= incr;
#ifndef NDEBUG
totalBytesProvidedBySBRK += incr;
#endif
xTaskResumeAll(); // Note: safe to use before FreeRTOS scheduler started, but not within an ISR
return (char *) previousHeapEnd;
}
//! non-reentrant sbrk uses is actually reentrant by using current context
// ... because the current _reent structure is pointed to by global _impure_ptr
char * sbrk(int incr) { return _sbrk_r(_impure_ptr, incr); }
//! _sbrk is a synonym for sbrk.
char * _sbrk(int incr) { return sbrk(incr); }
void __malloc_lock(struct _reent *p) { (void)p; configASSERT( !xPortIsInsideInterrupt() ); // Make damn sure no mallocs inside ISRs!!
vTaskSuspendAll(); }
void __malloc_unlock(struct _reent *p) { (void)p; (void)xTaskResumeAll(); }
// newlib also requires implementing locks for the application's environment memory space,
// accessed by newlib's setenv() and getenv() functions.
// As these are trivial functions, momentarily suspend task switching (rather than semaphore).
// Not required (and trimmed by linker) in applications not using environment variables.
// ToDo: Move __env_lock/unlock to a separate newlib helper file.
void __env_lock(void) { vTaskSuspendAll(); }
void __env_unlock(void) { (void)xTaskResumeAll(); }
#if 1 // Provide malloc debug and accounting wrappers
/// /brief Wrap malloc/malloc_r to help debug who requests memory and why.
/// To use these, add linker options: -Xlinker --wrap=malloc -Xlinker --wrap=_malloc_r
// Note: These functions are normally unused and stripped by linker.
size_t TotalMallocdBytes;
int MallocCallCnt;
static bool inside_malloc;
void *__wrap_malloc(size_t nbytes) {
extern void * __real_malloc(size_t nbytes);
MallocCallCnt++;
TotalMallocdBytes += nbytes;
inside_malloc = true;
void *p = __real_malloc(nbytes); // will call malloc_r...
inside_malloc = false;
return p;
}
void *__wrap__malloc_r(void *reent, size_t nbytes) {
extern void * __real__malloc_r(void *reent,size_t nbytes);
if(!inside_malloc) {
MallocCallCnt++;
TotalMallocdBytes += nbytes;
}
void *p = __real__malloc_r(reent,nbytes);
return p;
}
#endif
// ================================================================================================
// Implement FreeRTOS's memory API using newlib-provided malloc family.
// ================================================================================================
void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION {
void *p = malloc(xSize);
return p;
}
void vPortFree( void *pv ) PRIVILEGED_FUNCTION {
free(pv);
}
size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION {
struct mallinfo mi = mallinfo(); // available space now managed by newlib
return mi.fordblks + heapBytesRemaining; // plus space not yet handed to newlib by sbrk
}
// GetMinimumEverFree is not available in newlib's malloc implementation.
// So, no implementation is provided: size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION;
//! No implementation needed, but stub provided in case application already calls vPortInitialiseBlocks
void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION {}