1/*
2 * Copyright (c) 2020 Stephanos Ioannidis <root@stephanos.io>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7/**
8 * @file
9 * @brief Exception handlers for ARM Cortex-A and Cortex-R
10 *
11 * This file implements the exception handlers (undefined instruction, prefetch
12 * abort and data abort) for ARM Cortex-A and Cortex-R processors.
13 *
14 * All exception handlers save the exception stack frame into the exception
15 * mode stack rather than the system mode stack, in order to ensure predictable
16 * exception behaviour (i.e. an arbitrary thread stack overflow cannot cause
17 * exception handling and thereby subsequent total system failure).
18 *
19 * In case the exception is due to a fatal (unrecoverable) fault, the fault
20 * handler is responsible for invoking the architecture fatal exception handler
21 * (z_arm_fatal_error) which invokes the kernel fatal exception handler
22 * (z_fatal_error) that either locks up the system or aborts the current thread
23 * depending on the application exception handler implementation.
24 */
25
26#include <zephyr/toolchain.h>
27#include <zephyr/linker/sections.h>
28#include <offsets_short.h>
29#include <zephyr/arch/cpu.h>
30#include "macro_priv.inc"
31
32_ASM_FILE_PROLOGUE
33
34#if defined(CONFIG_FPU_SHARING)
35GTEXT(z_arm_fault_undef_instruction_fp)
36#endif
37GTEXT(z_arm_fault_undef_instruction)
38GTEXT(z_arm_fault_prefetch)
39GTEXT(z_arm_fault_data)
40
41GTEXT(z_arm_undef_instruction)
42GTEXT(z_arm_prefetch_abort)
43GTEXT(z_arm_data_abort)
44
45#ifndef CONFIG_USE_SWITCH
46
47.macro exception_entry mode
48	/*
49	 * Store r0-r3, r12, lr, lr_und and spsr_und into the stack to
50	 * construct an exception stack frame.
51	 */
52	srsdb sp!, #\mode
53	stmfd sp, {r0-r3, r12, lr}^
54	sub sp, #24
55
56#if defined(CONFIG_FPU_SHARING)
57	sub sp, #___fpu_t_SIZEOF
58
59	vmrs r1, fpexc
60	mov r0, #FPEXC_EN
61	vmsr fpexc, r0
62	vmrs r0, fpscr
63
64	mov r2, sp
65	vstmia r2!, {s0-s15}
66#ifdef CONFIG_VFP_FEATURE_REGS_S64_D32
67	vstmia r2!, {d16-d31}
68#endif
69	stm r2, {r0, r1}
70#endif
71
72#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
73	/* Pointer to extra esf info */
74	sub sp, #___extra_esf_info_t_SIZEOF
75	mov r0, #0
76	str r0, [sp, #4]
77	str r0, [sp, #8]
78
79	sub r1, sp, #___callee_saved_t_SIZEOF
80	str r1, [sp]
81	cps #MODE_SYS
82	stm r1, {r4-r11, sp}
83	cps #\mode
84
85	mov r0, sp
86	mov sp, r1
87#else
88	mov r0, sp
89#endif
90
91	/* Increment exception nesting count */
92	get_cpu r2
93	ldr r1, [r2, #___cpu_t_nested_OFFSET]
94	add r1, r1, #1
95	str r1, [r2, #___cpu_t_nested_OFFSET]
96.endm
97
98.macro exception_exit
99	/* Exit exception */
100#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
101	add sp, #___extra_esf_info_t_SIZEOF
102	add sp, #___callee_saved_t_SIZEOF
103#endif
104.endm
105
106/**
107 * @brief Undefined instruction exception handler
108 *
109 * An undefined instruction (UNDEF) exception is generated when an undefined
110 * instruction, or a VFP instruction when the VFP is not enabled, is
111 * encountered.
112 */
113SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_undef_instruction)
114	/*
115	 * The undefined instruction address is offset by 2 if the previous
116	 * mode is Thumb; otherwise, it is offset by 4.
117	 */
118	push {r0}
119	mrs r0, spsr
120	tst r0, #T_BIT
121	subeq lr, #4	/* ARM   (!T_BIT) */
122	subne lr, #2	/* Thumb (T_BIT) */
123	pop {r0}
124
125	/*
126	 * Store r0-r3, r12, lr, lr_und and spsr_und into the stack to
127	 * construct an exception stack frame.
128	 */
129	srsdb sp!, #MODE_UND
130	stmfd sp, {r0-r3, r12, lr}^
131	sub sp, #24
132
133	/* Increment exception nesting count */
134	get_cpu r2
135	ldr r1, [r2, #___cpu_t_nested_OFFSET]
136	add r1, r1, #1
137	str r1, [r2, #___cpu_t_nested_OFFSET]
138
139#if defined(CONFIG_FPU_SHARING)
140	sub sp, #___fpu_t_SIZEOF
141
142	bl z_arm_fault_undef_instruction_fp
143	cmp r0, #0
144	beq z_arm_exc_exit
145
146	vmrs r1, fpexc
147	mov r0, #FPEXC_EN
148	vmsr fpexc, r0
149	vmrs r0, fpscr
150
151	mov r2, sp
152	vstmia r2!, {s0-s15}
153#ifdef CONFIG_VFP_FEATURE_REGS_S64_D32
154	vstmia r2!, {d16-d31}
155#endif
156	stm r2, {r0, r1}
157#endif
158
159#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
160	/* Pointer to extra esf info */
161	sub sp, #___extra_esf_info_t_SIZEOF
162	mov r0, #0
163	str r0, [sp, #4]
164	str r0, [sp, #8]
165
166	sub r1, sp, #___callee_saved_t_SIZEOF
167	str r1, [sp]
168	cps #MODE_SYS
169	stm r1, {r4-r11, sp}
170	cps #MODE_UND
171
172	mov r0, sp
173	mov sp, r1
174#else
175	mov r0, sp
176#endif
177
178	bl z_arm_fault_undef_instruction
179	exception_exit
180
181	b z_arm_exc_exit
182
183/**
184 * @brief Prefetch abort exception handler
185 *
186 * A prefetch abort (PABT) exception is generated when the processor marks the
187 * prefetched instruction as invalid and the instruction is executed.
188 */
189SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_prefetch_abort)
190	/*
191	 * The faulting instruction address is always offset by 4 for the
192	 * prefetch abort exceptions.
193	 */
194	sub lr, #4
195
196	exception_entry MODE_ABT
197	bl z_arm_fault_prefetch
198	exception_exit
199
200	b z_arm_exc_exit
201
202#if defined(CONFIG_FPU_SHARING)
203#define FPU_SF_SIZE	___fpu_t_SIZEOF
204#else
205#define FPU_SF_SIZE	0
206#endif
207
208/**
209 * @brief Data abort exception handler
210 *
211 * A data abort (DABT) exception is generated when an error occurs on a data
212 * memory access. This exception can be either synchronous or asynchronous,
213 * depending on the type of fault that caused it.
214 */
215SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_data_abort)
216	/*
217	 * The faulting instruction address is always offset by 8 for the data
218	 * abort exceptions.
219	 */
220	sub lr, #8
221
222	exception_entry MODE_ABT
223	bl z_arm_fault_data
224
225	/*
226	 * If z_arm_fault_data returns false, then we recovered from
227	 * the error.  It may have updated $pc, so copy $pc back to
228	 * the true esf from the one passed to z_arm_fault_data.
229	 */
230	cmp r0, #0
231	ldreq r1, [sp, #24 + FPU_SF_SIZE]
232
233	exception_exit
234
235	streq r1, [sp, #24 + FPU_SF_SIZE]
236
237	b z_arm_exc_exit
238
239#else
240/**
241 * @brief Undefined instruction exception handler
242 *
243 * An undefined instruction (UNDEF) exception is generated when an undefined
244 * instruction, or a VFP instruction when the VFP is not enabled, is
245 * encountered.
246 */
247SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_undef_instruction)
248	/*
249	 * The undefined instruction address is offset by 2 if the previous
250	 * mode is Thumb; otherwise, it is offset by 4.
251	 */
252	push {r0}
253	mrs r0, spsr
254	tst r0, #T_BIT
255	subeq lr, #4	/* ARM   (!T_BIT) */
256	subne lr, #2	/* Thumb (T_BIT) */
257	pop {r0}
258
259	z_arm_cortex_ar_enter_exc
260	bl z_arm_fault_undef_instruction
261	b z_arm_cortex_ar_exit_exc
262
263/**
264 * @brief Prefetch abort exception handler
265 *
266 * A prefetch abort (PABT) exception is generated when the processor marks the
267 * prefetched instruction as invalid and the instruction is executed.
268 */
269SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_prefetch_abort)
270	/*
271	 * The faulting instruction address is always offset by 4 for the
272	 * prefetch abort exceptions.
273	 */
274	sub lr, #4
275	z_arm_cortex_ar_enter_exc
276	bl z_arm_fault_prefetch
277	b z_arm_cortex_ar_exit_exc
278
279/**
280 * @brief Data abort exception handler
281 *
282 * A data abort (DABT) exception is generated when an error occurs on a data
283 * memory access. This exception can be either synchronous or asynchronous,
284 * depending on the type of fault that caused it.
285 */
286SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_data_abort)
287	sub lr, #8
288
289	z_arm_cortex_ar_enter_exc
290	bl z_arm_fault_data
291	b z_arm_cortex_ar_exit_exc
292
293#endif
294