1 /*
2  * SPDX-License-Identifier: BSD-3-Clause
3  *
4  * Copyright © 2019 Keith Packard
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above
14  *    copyright notice, this list of conditions and the following
15  *    disclaimer in the documentation and/or other materials provided
16  *    with the distribution.
17  *
18  * 3. Neither the name of the copyright holder nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
33  * OF THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include "../../crt0.h"
37 
38 #if __ARM_ARCH_PROFILE == 'M'
39 
40 /*
41  * Cortex-mM includes an NVIC and starts with SP initialized, so start
42  * is a C function
43  */
44 
45 extern const void *__interrupt_vector[];
46 
47 #define CPACR	((volatile uint32_t *) (0xE000ED88))
48 
49 #ifdef __clang__
50 const void *__interrupt_reference = __interrupt_vector;
51 #endif
52 
53 void
_start(void)54 _start(void)
55 {
56 	/* Generate a reference to __interrupt_vector so we get one loaded */
57 	__asm__(".equ __my_interrupt_vector, __interrupt_vector");
58 #ifndef __SOFTFP__
59 	/* Enable FPU */
60 	*CPACR |= 0xf << 20;
61 	/*
62 	 * Wait for the write enabling FPU to reach memory before
63 	 * executing the instruction accessing the status register
64 	 */
65 	__asm__("dsb");
66 	__asm__("isb");
67 
68         /* Clear FPU status register. 0x40000 will initialize FPSCR.LTPSIZE to
69          * a valid value for 8.1-m low overhead loops. */
70 #if __ARM_ARCH >= 8 && __ARM_ARCH_PROFILE == 'M'
71 #define INIT_FPSCR 0x40000
72 #else
73 #define INIT_FPSCR 0x0
74 #endif
75 	__asm__("vmsr fpscr, %0" : : "r" (INIT_FPSCR));
76 #endif
77 	__start();
78 }
79 
80 #else
81 
82 /*
83  * Regular ARM has an 8-entry exception vector and starts without SP
84  * initialized, so start is a naked function
85  */
86 
87 static void __attribute__((used)) __section(".init")
_cstart(void)88 _cstart(void)
89 {
90 	__start();
91 }
92 
93 extern char __stack[];
94 
95 void __attribute__((naked)) __section(".init") __attribute__((used))
_start(void)96 _start(void)
97 {
98 	/* Generate a reference to __vector_table so we get one loaded */
99 	__asm__(".equ __my_vector_table, __vector_table");
100 
101 	/* Initialize stack pointer */
102 	__asm__("mov sp, %0" : : "r" (__stack));
103 
104 #if __ARM_ARCH == 7
105 #ifdef __thumb__
106 	/* Make exceptions run in Thumb mode */
107 	uint32_t sctlr;
108 	__asm__("mrc p15, 0, %0, c1, c0, 0" : "=r" (sctlr));
109 	sctlr |= (1 << 30);
110 	__asm__("mcr p15, 0, %0, c1, c0, 0" : : "r" (sctlr));
111 #endif
112 #ifndef __SOFTFP__
113 	/* Set CPACR for access to CP10 and 11 */
114 	__asm__("mcr p15, 0, %0, c1, c0, 2" : : "r" (0xf << 20));
115 	/* Enable FPU */
116 	__asm__("vmsr fpexc, %0" : : "r" (0x40000000));
117 #endif
118 #endif
119 
120 	/* Branch to C code */
121 	__asm__("b _cstart");
122 }
123 
124 #endif
125 
126 #ifdef CRT0_SEMIHOST
127 
128 /*
129  * Trap faults, print message and exit when running under semihost
130  */
131 
132 #include <semihost.h>
133 #include <unistd.h>
134 #include <stdio.h>
135 
136 #define _REASON(r) #r
137 #define REASON(r) _REASON(r)
138 
139 #if __ARM_ARCH_PROFILE == 'M'
140 
141 #define GET_SP  struct fault *sp; __asm__ ("mov %0, sp" : "=r" (sp))
142 
143 struct fault {
144     unsigned int        r0;
145     unsigned int        r1;
146     unsigned int        r2;
147     unsigned int        r3;
148     unsigned int        r12;
149     unsigned int        lr;
150     unsigned int        pc;
151     unsigned int        xpsr;
152 };
153 
154 static const char *const reasons[] = {
155     "hardfault",
156     "memmanage",
157     "busfault",
158     "usagefault"
159 };
160 
161 #define REASON_HARDFAULT        0
162 #define REASON_MEMMANAGE        1
163 #define REASON_BUSFAULT         2
164 #define REASON_USAGE            3
165 
166 static void __attribute__((used))
arm_fault(struct fault * f,int reason)167 arm_fault(struct fault *f, int reason)
168 {
169     printf("ARM fault: %s\n", reasons[reason]);
170     printf("\tR0:   0x%08x\n", f->r0);
171     printf("\tR1:   0x%08x\n", f->r1);
172     printf("\tR2:   0x%08x\n", f->r2);
173     printf("\tR3:   0x%08x\n", f->r3);
174     printf("\tR12:  0x%08x\n", f->r12);
175     printf("\tLR:   0x%08x\n", f->lr);
176     printf("\tPC:   0x%08x\n", f->pc);
177     printf("\tXPSR: 0x%08x\n", f->xpsr);
178     _exit(1);
179 }
180 
181 void __attribute__((naked))
arm_hardfault_isr(void)182 arm_hardfault_isr(void)
183 {
184     __asm__("mov r0, sp");
185     __asm__("movs r1, #" REASON(REASON_HARDFAULT));
186     __asm__("bl  arm_fault");
187 }
188 
189 void __attribute__((naked))
arm_memmange_isr(void)190 arm_memmange_isr(void)
191 {
192     __asm__("mov r0, sp");
193     __asm__("movs r1, #" REASON(REASON_MEMMANAGE));
194     __asm__("bl  arm_fault");
195 }
196 
197 void __attribute__((naked))
arm_busfault_isr(void)198 arm_busfault_isr(void)
199 {
200     __asm__("mov r0, sp");
201     __asm__("movs r1, #" REASON(REASON_BUSFAULT));
202     __asm__("bl  arm_fault");
203 }
204 
205 void __attribute__((naked))
arm_usagefault_isr(void)206 arm_usagefault_isr(void)
207 {
208     __asm__("mov r0, sp");
209     __asm__("movs r1, #" REASON(REASON_USAGE));
210     __asm__("bl  arm_fault");
211 }
212 
213 #else
214 
215 struct fault {
216     unsigned int        r[7];
217     unsigned int        pc;
218 };
219 
220 static const char *const reasons[] = {
221     "undef",
222     "svc",
223     "prefetch_abort",
224     "data_abort"
225 };
226 
227 #define REASON_UNDEF            0
228 #define REASON_SVC              1
229 #define REASON_PREFETCH_ABORT   2
230 #define REASON_DATA_ABORT       3
231 
232 static void __attribute__((used))
arm_fault(struct fault * f,int reason)233 arm_fault(struct fault *f, int reason)
234 {
235     int r;
236     printf("ARM fault: %s\n", reasons[reason]);
237     for (r = 0; r <= 6; r++)
238         printf("\tR%d:   0x%08x\n", r, f->r[r]);
239     printf("\tPC:   0x%08x\n", f->pc);
240     _exit(1);
241 }
242 
243 #define VECTOR_COMMON \
244     __asm__("mov r7, %0" : : "r" (__stack)); \
245     __asm__("mov sp, r7"); \
246     __asm__("push {lr}"); \
247     __asm__("push {r0-r6}"); \
248     __asm__("mov r0, sp")
249 
250 void __attribute__((naked)) __section(".init")
arm_undef_vector(void)251 arm_undef_vector(void)
252 {
253     VECTOR_COMMON;
254     __asm__("mov r1, #" REASON(REASON_UNDEF));
255     __asm__("bl  arm_fault");
256 }
257 
258 void __attribute__((naked)) __section(".init")
arm_prefetch_abort_vector(void)259 arm_prefetch_abort_vector(void)
260 {
261     VECTOR_COMMON;
262     __asm__("mov r1, #" REASON(REASON_UNDEF));
263     __asm__("bl  arm_fault");
264 }
265 
266 void __attribute__((naked)) __section(".init")
arm_data_abort_vector(void)267 arm_data_abort_vector(void)
268 {
269     VECTOR_COMMON;
270     __asm__("mov r1, #" REASON(REASON_UNDEF));
271     __asm__("bl  arm_fault");
272 }
273 
274 #endif
275 
276 #endif
277 
278