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