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; |
cdefb14d AG |
37 | extern "C" unsigned int __HeapBase; |
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 | ||
184 | extern "C" int __aeabi_unwind_cpp_pr0(int state, void* controlBlock, void* context) | |
185 | { | |
186 | abort(); | |
187 | } | |
188 | ||
189 | ||
190 | extern "C" int __aeabi_unwind_cpp_pr1(int state, void* controlBlock, void* context) | |
191 | { | |
192 | abort(); | |
193 | } | |
194 | ||
195 | ||
196 | extern "C" int __aeabi_unwind_cpp_pr2(int state, void* controlBlock, void* context) | |
197 | { | |
198 | abort(); | |
199 | } | |
200 | ||
201 | /* Trap calls to malloc/free/realloc in ISR. */ | |
202 | extern "C" void __malloc_lock(void) | |
203 | { | |
204 | if (__get_IPSR() != 0) | |
205 | __debugbreak(); | |
206 | } | |
207 | ||
208 | extern "C" void __malloc_unlock(void) | |
209 | { | |
210 | } | |
211 | ||
212 | ||
60c34c05 AG |
213 | /* Turn off the errno macro and use actual external global variable instead. */ |
214 | #undef errno | |
215 | extern int errno; | |
216 | ||
ddf5038e AG |
217 | static int doesHeapCollideWithStack(unsigned int newHeap); |
218 | ||
60c34c05 AG |
219 | /* Dynamic memory allocation related syscalls. */ |
220 | extern "C" caddr_t _sbrk(int incr) | |
221 | { | |
222 | static unsigned char* heap = (unsigned char*)&__HeapBase; | |
223 | unsigned char* prev_heap = heap; | |
224 | unsigned char* new_heap = heap + incr; | |
225 | ||
ddf5038e AG |
226 | if (doesHeapCollideWithStack((unsigned int)new_heap)) |
227 | { | |
60c34c05 AG |
228 | errno = ENOMEM; |
229 | return (caddr_t)-1; | |
230 | } | |
231 | ||
232 | heap = new_heap; | |
233 | return (caddr_t) prev_heap; | |
234 | } | |
235 | ||
ddf5038e AG |
236 | static int doesHeapCollideWithStack(unsigned int newHeap) |
237 | { | |
238 | return ((newHeap >= __get_MSP()) || | |
239 | (STACK_SIZE && newHeap >= g_maximumHeapAddress)); | |
240 | } | |
241 | ||
60c34c05 AG |
242 | |
243 | /* Optional functionality which will tag each heap allocation with the caller's return address. */ | |
b3e1627c | 244 | #ifdef HEAP_TAGS |
60c34c05 AG |
245 | |
246 | const unsigned int* __smoothieHeapBase = &__HeapBase; | |
247 | ||
248 | extern "C" void* __real_malloc(size_t size); | |
249 | extern "C" void* __real_realloc(void* ptr, size_t size); | |
b3e1627c | 250 | extern "C" void __real_free(void* ptr); |
60c34c05 AG |
251 | |
252 | static void setTag(void* pv, unsigned int tag); | |
253 | static unsigned int* footerForChunk(void* pv); | |
254 | static unsigned int* headerForChunk(void* pv); | |
255 | static unsigned int sizeOfChunk(unsigned int* pHeader); | |
b3e1627c | 256 | static int isChunkInUse(void* pv); |
60c34c05 AG |
257 | |
258 | extern "C" __attribute__((naked)) void __wrap_malloc(size_t size) | |
259 | { | |
260 | __asm ( | |
261 | ".syntax unified\n" | |
262 | ".thumb\n" | |
263 | "mov r1,lr\n" | |
264 | "b mallocWithTag\n" | |
265 | ); | |
266 | } | |
267 | ||
268 | extern "C" void* mallocWithTag(size_t size, unsigned int tag) | |
269 | { | |
270 | void* p = __real_malloc(size + sizeof(tag)); | |
271 | if (!p && __smoothieHeapBase) | |
272 | return p; | |
273 | setTag(p, tag); | |
274 | return p; | |
275 | } | |
276 | ||
277 | static void setTag(void* pv, unsigned int tag) | |
278 | { | |
279 | unsigned int* pFooter = footerForChunk(pv); | |
280 | *pFooter = tag; | |
281 | } | |
282 | ||
283 | static unsigned int* footerForChunk(void* pv) | |
284 | { | |
285 | unsigned int* pHeader = headerForChunk(pv); | |
286 | unsigned int size = sizeOfChunk(pHeader); | |
287 | return (unsigned int*)(void*)((char*)pHeader + size); | |
288 | } | |
289 | ||
290 | static unsigned int* headerForChunk(void* pv) | |
291 | { | |
292 | // Header is allocated two words (8 bytes) before the publicly returned allocation chunk address. | |
293 | unsigned int* p = (unsigned int*)pv; | |
294 | return &p[-2]; | |
295 | } | |
296 | ||
297 | static unsigned int sizeOfChunk(unsigned int* pHeader) | |
298 | { | |
299 | /* Remove previous chunk in use flag. */ | |
300 | return pHeader[1] & ~1; | |
301 | } | |
302 | ||
303 | extern "C" __attribute__((naked)) void __wrap_realloc(void* ptr, size_t size) | |
304 | { | |
305 | __asm ( | |
306 | ".syntax unified\n" | |
307 | ".thumb\n" | |
308 | "mov r2,lr\n" | |
309 | "b reallocWithTag\n" | |
310 | ); | |
311 | } | |
312 | ||
313 | extern "C" void* reallocWithTag(void* ptr, size_t size, unsigned int tag) | |
314 | { | |
315 | void* p = __real_realloc(ptr, size + sizeof(tag)); | |
316 | if (!p) | |
317 | return p; | |
318 | setTag(p, tag); | |
319 | return p; | |
320 | } | |
321 | ||
b3e1627c AG |
322 | extern "C" void __wrap_free(void* ptr) |
323 | { | |
324 | if (!isChunkInUse(ptr)) | |
325 | __debugbreak(); | |
326 | __real_free(ptr); | |
327 | } | |
328 | ||
329 | static int isChunkInUse(void* pv) | |
330 | { | |
331 | unsigned int* pFooter = footerForChunk(pv); | |
332 | return pFooter[1] & 1; | |
333 | } | |
334 | ||
60c34c05 AG |
335 | __attribute__((naked)) void* operator new(size_t size) |
336 | { | |
337 | __asm ( | |
338 | ".syntax unified\n" | |
339 | ".thumb\n" | |
340 | "push {r4,lr}\n" | |
341 | "mov r1,lr\n" | |
342 | "bl mallocWithTag\n" | |
343 | "cbnz r0, 1$\n" | |
344 | "bl abort\n" | |
345 | "1$:\n" | |
346 | "pop {r4,pc}\n" | |
347 | ); | |
b3e1627c AG |
348 | // This line never executes but silences no return value warning from compiler. |
349 | return (void*)1; | |
60c34c05 AG |
350 | } |
351 | ||
352 | #endif // HEAP_TAGS |