Commit | Line | Data |
---|---|---|
ae159ef4 | 1 | /* Copyright 2013 Adam Green (http://mbed.org/users/AdamGreen/) |
60c34c05 | 2 | |
ae159ef4 AG |
3 | Licensed under the Apache License, Version 2.0 (the "License"); |
4 | you may not use this file except in compliance with the License. | |
5 | You may obtain a copy of the License at | |
60c34c05 | 6 | |
ae159ef4 | 7 | http://www.apache.org/licenses/LICENSE-2.0 |
60c34c05 | 8 | |
ae159ef4 AG |
9 | Unless required by applicable law or agreed to in writing, software |
10 | distributed under the License is distributed on an "AS IS" BASIS, | |
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | See the License for the specific language governing permissions and | |
13 | limitations under the License. | |
60c34c05 AG |
14 | */ |
15 | /* Provide routines which hook the MRI debug monitor into GCC4MBED projects. */ | |
16 | #include <string.h> | |
17 | #include <sys/types.h> | |
18 | #include <errno.h> | |
19 | #include <mri.h> | |
20 | #include <cmsis.h> | |
65eee97a AG |
21 | #include "mpu.h" |
22 | ||
cdefb14d | 23 | |
ddf5038e AG |
24 | static unsigned int g_maximumHeapAddress; |
25 | ||
cdefb14d | 26 | static void fillUnusedRAM(void); |
ddf5038e AG |
27 | static void configureStackSizeLimit(unsigned int stackSizeLimit); |
28 | static unsigned int alignTo32Bytes(unsigned int value); | |
29 | static void configureMpuToCatchStackOverflowIntoHeap(unsigned int maximumHeapAddress); | |
30 | static void configureMpuRegionToAccessAllMemoryWithNoCaching(void); | |
60c34c05 | 31 | |
cdefb14d | 32 | |
ddf5038e | 33 | /* Symbols exposed from the linker script. */ |
cdefb14d AG |
34 | extern unsigned int __bss_start__; |
35 | extern unsigned int __bss_end__; | |
ddf5038e | 36 | extern unsigned int __StackTop; |
ec7b59de | 37 | extern "C" unsigned int __end__; |
cdefb14d AG |
38 | |
39 | ||
60c34c05 AG |
40 | extern "C" int main(void); |
41 | extern "C" void __libc_init_array(void); | |
42 | extern "C" void exit(int ErrorCode); | |
43 | extern "C" void _start(void) | |
44 | { | |
45 | int bssSize = (int)&__bss_end__ - (int)&__bss_start__; | |
46 | int mainReturnValue; | |
47 | ||
48 | memset(&__bss_start__, 0, bssSize); | |
cdefb14d | 49 | fillUnusedRAM(); |
ddf5038e AG |
50 | |
51 | if (STACK_SIZE) | |
52 | { | |
53 | configureStackSizeLimit(STACK_SIZE); | |
54 | } | |
65eee97a AG |
55 | if (WRITE_BUFFER_DISABLE) |
56 | { | |
57 | disableMPU(); | |
ddf5038e | 58 | configureMpuRegionToAccessAllMemoryWithNoCaching(); |
65eee97a AG |
59 | enableMPU(); |
60 | } | |
60c34c05 AG |
61 | if (MRI_ENABLE) |
62 | { | |
63 | __mriInit(MRI_INIT_PARAMETERS); | |
64 | if (MRI_BREAK_ON_INIT) | |
65 | __debugbreak(); | |
66 | } | |
cdefb14d | 67 | |
60c34c05 AG |
68 | __libc_init_array(); |
69 | mainReturnValue = main(); | |
70 | exit(mainReturnValue); | |
71 | } | |
72 | ||
cdefb14d AG |
73 | static __attribute__((naked)) void fillUnusedRAM(void) |
74 | { | |
75 | __asm ( | |
76 | ".syntax unified\n" | |
77 | ".thumb\n" | |
78 | // Fill 2 words (8 bytes) at a time with 0xdeadbeef. | |
79 | " ldr r2, =__FillStart\n" | |
80 | " movw r0, #0xbeef\n" | |
81 | " movt r0, #0xdead\n" | |
82 | " mov r1, r0\n" | |
83 | // Don't fill past current stack pointer value. | |
84 | " mov r3, sp\n" | |
85 | " bics r3, r3, #7\n" | |
86 | "1$:\n" | |
87 | " strd r0, r1, [r2], #8\n" | |
88 | " cmp r2, r3\n" | |
89 | " blo 1$\n" | |
90 | " bx lr\n" | |
91 | ); | |
92 | } | |
93 | ||
ddf5038e AG |
94 | static void configureStackSizeLimit(unsigned int stackSizeLimit) |
95 | { | |
96 | // Note: 32 bytes are reserved to fall between top of heap and top of stack for minimum MPU guard region. | |
97 | g_maximumHeapAddress = alignTo32Bytes((unsigned int)&__StackTop - stackSizeLimit - 32); | |
98 | configureMpuToCatchStackOverflowIntoHeap(g_maximumHeapAddress); | |
99 | } | |
100 | ||
101 | static unsigned int alignTo32Bytes(unsigned int value) | |
102 | { | |
103 | return (value + 31) & ~31; | |
104 | } | |
105 | ||
106 | static void configureMpuToCatchStackOverflowIntoHeap(unsigned int maximumHeapAddress) | |
107 | { | |
108 | #define MPU_REGION_SIZE_OF_32_BYTES ((5-1) << MPU_RASR_SIZE_SHIFT) // 2^5 = 32 bytes. | |
109 | ||
110 | prepareToAccessMPURegion(getHighestMPUDataRegionIndex()); | |
111 | setMPURegionAddress(maximumHeapAddress); | |
112 | setMPURegionAttributeAndSize(MPU_REGION_SIZE_OF_32_BYTES | MPU_RASR_ENABLE); | |
113 | enableMPUWithDefaultMemoryMap(); | |
114 | } | |
cdefb14d | 115 | |
ddf5038e | 116 | static void configureMpuRegionToAccessAllMemoryWithNoCaching(void) |
65eee97a AG |
117 | { |
118 | static const uint32_t regionToStartAtAddress0 = 0U; | |
119 | static const uint32_t regionReadWrite = 1 << MPU_RASR_AP_SHIFT; | |
120 | static const uint32_t regionSizeAt4GB = 31 << MPU_RASR_SIZE_SHIFT; /* 4GB = 2^(31+1) */ | |
121 | static const uint32_t regionEnable = MPU_RASR_ENABLE; | |
122 | static const uint32_t regionSizeAndAttributes = regionReadWrite | regionSizeAt4GB | regionEnable; | |
ddf5038e | 123 | uint32_t regionIndex = STACK_SIZE ? getHighestMPUDataRegionIndex() - 1 : getHighestMPUDataRegionIndex(); |
65eee97a | 124 | |
ddf5038e | 125 | prepareToAccessMPURegion(regionIndex); |
65eee97a AG |
126 | setMPURegionAddress(regionToStartAtAddress0); |
127 | setMPURegionAttributeAndSize(regionSizeAndAttributes); | |
128 | } | |
129 | ||
130 | ||
60c34c05 AG |
131 | extern "C" int __real__read(int file, char *ptr, int len); |
132 | extern "C" int __wrap__read(int file, char *ptr, int len) | |
133 | { | |
134 | if (MRI_SEMIHOST_STDIO && file < 3) | |
135 | return __mriNewlib_SemihostRead(file, ptr, len); | |
136 | return __real__read(file, ptr, len); | |
137 | } | |
138 | ||
139 | ||
140 | extern "C" int __real__write(int file, char *ptr, int len); | |
141 | extern "C" int __wrap__write(int file, char *ptr, int len) | |
142 | { | |
143 | if (MRI_SEMIHOST_STDIO && file < 3) | |
144 | return __mriNewlib_SemihostWrite(file, ptr, len); | |
145 | return __real__write(file, ptr, len); | |
146 | } | |
147 | ||
148 | ||
149 | extern "C" int __real__isatty(int file); | |
150 | extern "C" int __wrap__isatty(int file) | |
151 | { | |
152 | /* Hardcoding the stdin/stdout/stderr handles to be interactive tty devices, unlike mbed.ar */ | |
153 | if (file < 3) | |
154 | return 1; | |
155 | return __real__isatty(file); | |
156 | } | |
157 | ||
158 | ||
159 | extern "C" int __wrap_semihost_connected(void) | |
160 | { | |
161 | /* MRI makes it look like there is no mbed interface attached since it disables the JTAG portion but MRI does | |
162 | support some of the mbed semihost calls when it is running so force it to return -1, indicating that the | |
163 | interface is attached. */ | |
164 | return -1; | |
165 | } | |
166 | ||
167 | ||
168 | ||
169 | extern "C" void abort(void) | |
170 | { | |
171 | if (MRI_ENABLE) | |
172 | __debugbreak(); | |
173 | ||
174 | exit(1); | |
175 | } | |
176 | ||
177 | ||
178 | extern "C" void __cxa_pure_virtual(void) | |
179 | { | |
180 | abort(); | |
181 | } | |
182 | ||
183 | ||
60c34c05 AG |
184 | /* Trap calls to malloc/free/realloc in ISR. */ |
185 | extern "C" void __malloc_lock(void) | |
186 | { | |
187 | if (__get_IPSR() != 0) | |
188 | __debugbreak(); | |
189 | } | |
190 | ||
191 | extern "C" void __malloc_unlock(void) | |
192 | { | |
193 | } | |
194 | ||
195 | ||
60c34c05 AG |
196 | /* Turn off the errno macro and use actual external global variable instead. */ |
197 | #undef errno | |
198 | extern int errno; | |
199 | ||
ddf5038e AG |
200 | static int doesHeapCollideWithStack(unsigned int newHeap); |
201 | ||
60c34c05 AG |
202 | /* Dynamic memory allocation related syscalls. */ |
203 | extern "C" caddr_t _sbrk(int incr) | |
204 | { | |
ec7b59de | 205 | static unsigned char* heap = (unsigned char*)&__end__; |
60c34c05 AG |
206 | unsigned char* prev_heap = heap; |
207 | unsigned char* new_heap = heap + incr; | |
208 | ||
ddf5038e AG |
209 | if (doesHeapCollideWithStack((unsigned int)new_heap)) |
210 | { | |
60c34c05 AG |
211 | errno = ENOMEM; |
212 | return (caddr_t)-1; | |
213 | } | |
214 | ||
215 | heap = new_heap; | |
216 | return (caddr_t) prev_heap; | |
217 | } | |
218 | ||
ddf5038e AG |
219 | static int doesHeapCollideWithStack(unsigned int newHeap) |
220 | { | |
221 | return ((newHeap >= __get_MSP()) || | |
222 | (STACK_SIZE && newHeap >= g_maximumHeapAddress)); | |
223 | } | |
224 | ||
60c34c05 AG |
225 | |
226 | /* Optional functionality which will tag each heap allocation with the caller's return address. */ | |
b3e1627c | 227 | #ifdef HEAP_TAGS |
60c34c05 | 228 | |
ec7b59de | 229 | const unsigned int* __smoothieHeapBase = &__end__; |
60c34c05 AG |
230 | |
231 | extern "C" void* __real_malloc(size_t size); | |
232 | extern "C" void* __real_realloc(void* ptr, size_t size); | |
b3e1627c | 233 | extern "C" void __real_free(void* ptr); |
60c34c05 AG |
234 | |
235 | static void setTag(void* pv, unsigned int tag); | |
236 | static unsigned int* footerForChunk(void* pv); | |
237 | static unsigned int* headerForChunk(void* pv); | |
238 | static unsigned int sizeOfChunk(unsigned int* pHeader); | |
b3e1627c | 239 | static int isChunkInUse(void* pv); |
60c34c05 AG |
240 | |
241 | extern "C" __attribute__((naked)) void __wrap_malloc(size_t size) | |
242 | { | |
243 | __asm ( | |
244 | ".syntax unified\n" | |
245 | ".thumb\n" | |
246 | "mov r1,lr\n" | |
247 | "b mallocWithTag\n" | |
248 | ); | |
249 | } | |
250 | ||
251 | extern "C" void* mallocWithTag(size_t size, unsigned int tag) | |
252 | { | |
253 | void* p = __real_malloc(size + sizeof(tag)); | |
254 | if (!p && __smoothieHeapBase) | |
255 | return p; | |
256 | setTag(p, tag); | |
257 | return p; | |
258 | } | |
259 | ||
260 | static void setTag(void* pv, unsigned int tag) | |
261 | { | |
262 | unsigned int* pFooter = footerForChunk(pv); | |
263 | *pFooter = tag; | |
264 | } | |
265 | ||
266 | static unsigned int* footerForChunk(void* pv) | |
267 | { | |
268 | unsigned int* pHeader = headerForChunk(pv); | |
269 | unsigned int size = sizeOfChunk(pHeader); | |
270 | return (unsigned int*)(void*)((char*)pHeader + size); | |
271 | } | |
272 | ||
273 | static unsigned int* headerForChunk(void* pv) | |
274 | { | |
275 | // Header is allocated two words (8 bytes) before the publicly returned allocation chunk address. | |
276 | unsigned int* p = (unsigned int*)pv; | |
277 | return &p[-2]; | |
278 | } | |
279 | ||
280 | static unsigned int sizeOfChunk(unsigned int* pHeader) | |
281 | { | |
282 | /* Remove previous chunk in use flag. */ | |
283 | return pHeader[1] & ~1; | |
284 | } | |
285 | ||
286 | extern "C" __attribute__((naked)) void __wrap_realloc(void* ptr, size_t size) | |
287 | { | |
288 | __asm ( | |
289 | ".syntax unified\n" | |
290 | ".thumb\n" | |
291 | "mov r2,lr\n" | |
292 | "b reallocWithTag\n" | |
293 | ); | |
294 | } | |
295 | ||
296 | extern "C" void* reallocWithTag(void* ptr, size_t size, unsigned int tag) | |
297 | { | |
298 | void* p = __real_realloc(ptr, size + sizeof(tag)); | |
299 | if (!p) | |
300 | return p; | |
301 | setTag(p, tag); | |
302 | return p; | |
303 | } | |
304 | ||
b3e1627c AG |
305 | extern "C" void __wrap_free(void* ptr) |
306 | { | |
307 | if (!isChunkInUse(ptr)) | |
308 | __debugbreak(); | |
309 | __real_free(ptr); | |
310 | } | |
311 | ||
312 | static int isChunkInUse(void* pv) | |
313 | { | |
314 | unsigned int* pFooter = footerForChunk(pv); | |
315 | return pFooter[1] & 1; | |
316 | } | |
317 | ||
60c34c05 AG |
318 | __attribute__((naked)) void* operator new(size_t size) |
319 | { | |
320 | __asm ( | |
321 | ".syntax unified\n" | |
322 | ".thumb\n" | |
323 | "push {r4,lr}\n" | |
324 | "mov r1,lr\n" | |
325 | "bl mallocWithTag\n" | |
326 | "cbnz r0, 1$\n" | |
327 | "bl abort\n" | |
328 | "1$:\n" | |
329 | "pop {r4,pc}\n" | |
330 | ); | |
b3e1627c AG |
331 | // This line never executes but silences no return value warning from compiler. |
332 | return (void*)1; | |
60c34c05 AG |
333 | } |
334 | ||
b4e5c830 AG |
335 | #else |
336 | ||
337 | /* Wrap memory allocation routines to make sure that they aren't being called from interrupt handler. */ | |
338 | static void breakOnHeapOpFromInterruptHandler(void) | |
339 | { | |
340 | if (__get_IPSR() != 0) | |
341 | __debugbreak(); | |
342 | } | |
343 | ||
344 | extern "C" void* __real_malloc(size_t size); | |
345 | extern "C" void* __wrap_malloc(size_t size) | |
346 | { | |
347 | breakOnHeapOpFromInterruptHandler(); | |
348 | return __real_malloc(size); | |
349 | } | |
350 | ||
351 | ||
352 | extern "C" void* __real_realloc(void* ptr, size_t size); | |
353 | extern "C" void* __wrap_realloc(void* ptr, size_t size) | |
354 | { | |
355 | breakOnHeapOpFromInterruptHandler(); | |
356 | return __real_realloc(ptr, size); | |
357 | } | |
358 | ||
359 | ||
360 | extern "C" void __real_free(void* ptr); | |
361 | extern "C" void __wrap_free(void* ptr) | |
362 | { | |
363 | breakOnHeapOpFromInterruptHandler(); | |
364 | __real_free(ptr); | |
365 | } | |
366 | ||
60c34c05 | 367 | #endif // HEAP_TAGS |