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