blob: 8cec70b807e85db85584d90633a7dc735e013223 [file] [log] [blame]
/*
* Copyright (c) 2020 Stephanos Ioannidis <root@stephanos.io>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Exception handlers for ARM Cortex-A and Cortex-R
*
* This file implements the exception handlers (undefined instruction, prefetch
* abort and data abort) for ARM Cortex-A and Cortex-R processors.
*
* All exception handlers save the exception stack frame into the exception
* mode stack rather than the system mode stack, in order to ensure predictable
* exception behaviour (i.e. an arbitrary thread stack overflow cannot cause
* exception handling and thereby subsequent total system failure).
*
* In case the exception is due to a fatal (unrecoverable) fault, the fault
* handler is responsible for invoking the architecture fatal exception handler
* (z_arm_fatal_error) which invokes the kernel fatal exception handler
* (z_fatal_error) that either locks up the system or aborts the current thread
* depending on the application exception handler implementation.
*/
#include <zephyr/toolchain.h>
#include <zephyr/linker/sections.h>
#include <offsets_short.h>
#include <zephyr/arch/cpu.h>
_ASM_FILE_PROLOGUE
#if defined(CONFIG_FPU_SHARING)
GTEXT(z_arm_fault_undef_instruction_fp)
#endif
GTEXT(z_arm_fault_undef_instruction)
GTEXT(z_arm_fault_prefetch)
GTEXT(z_arm_fault_data)
GTEXT(z_arm_undef_instruction)
GTEXT(z_arm_prefetch_abort)
GTEXT(z_arm_data_abort)
.macro exception_entry mode
/*
* Store r0-r3, r12, lr, lr_und and spsr_und into the stack to
* construct an exception stack frame.
*/
srsdb sp!, #\mode
stmfd sp, {r0-r3, r12, lr}^
sub sp, #24
#if defined(CONFIG_FPU_SHARING)
sub sp, #___fpu_t_SIZEOF
vmrs r1, fpexc
mov r0, #FPEXC_EN
vmsr fpexc, r0
vmrs r0, fpscr
mov r2, sp
vstmia r2!, {s0-s15}
stm r2, {r0, r1}
#endif
#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
/* Pointer to extra esf info */
sub sp, #___extra_esf_info_t_SIZEOF
mov r0, #0
str r0, [sp, #4]
str r0, [sp, #8]
sub r1, sp, #___callee_saved_t_SIZEOF
str r1, [sp]
cps #MODE_SYS
stm r1, {r4-r11, sp}
cps #\mode
mov r0, sp
mov sp, r1
#else
mov r0, sp
#endif
/* Increment exception nesting count */
ldr r2, =_kernel
ldr r1, [r2, #_kernel_offset_to_nested]
add r1, r1, #1
str r1, [r2, #_kernel_offset_to_nested]
.endm
.macro exception_exit
/* Exit exception */
#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
add sp, #___extra_esf_info_t_SIZEOF
add sp, #___callee_saved_t_SIZEOF
#endif
.endm
/**
* @brief Undefined instruction exception handler
*
* An undefined instruction (UNDEF) exception is generated when an undefined
* instruction, or a VFP instruction when the VFP is not enabled, is
* encountered.
*/
SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_undef_instruction)
/*
* The undefined instruction address is offset by 2 if the previous
* mode is Thumb; otherwise, it is offset by 4.
*/
push {r0}
mrs r0, spsr
tst r0, #T_BIT
subeq lr, #4 /* ARM (!T_BIT) */
subne lr, #2 /* Thumb (T_BIT) */
pop {r0}
/*
* Store r0-r3, r12, lr, lr_und and spsr_und into the stack to
* construct an exception stack frame.
*/
srsdb sp!, #MODE_UND
stmfd sp, {r0-r3, r12, lr}^
sub sp, #24
/* Increment exception nesting count */
ldr r2, =_kernel
ldr r1, [r2, #_kernel_offset_to_nested]
add r1, r1, #1
str r1, [r2, #_kernel_offset_to_nested]
#if defined(CONFIG_FPU_SHARING)
sub sp, #___fpu_t_SIZEOF
bl z_arm_fault_undef_instruction_fp
cmp r0, #0
beq z_arm_exc_exit
vmrs r1, fpexc
mov r0, #FPEXC_EN
vmsr fpexc, r0
vmrs r0, fpscr
mov r2, sp
vstmia r2!, {s0-s15}
stm r2, {r0, r1}
#endif
#if defined(CONFIG_EXTRA_EXCEPTION_INFO)
/* Pointer to extra esf info */
sub sp, #___extra_esf_info_t_SIZEOF
mov r0, #0
str r0, [sp, #4]
str r0, [sp, #8]
sub r1, sp, #___callee_saved_t_SIZEOF
str r1, [sp]
cps #MODE_SYS
stm r1, {r4-r11, sp}
cps #MODE_UND
mov r0, sp
mov sp, r1
#else
mov r0, sp
#endif
bl z_arm_fault_undef_instruction
exception_exit
b z_arm_exc_exit
/**
* @brief Prefetch abort exception handler
*
* A prefetch abort (PABT) exception is generated when the processor marks the
* prefetched instruction as invalid and the instruction is executed.
*/
SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_prefetch_abort)
/*
* The faulting instruction address is always offset by 4 for the
* prefetch abort exceptions.
*/
sub lr, #4
exception_entry MODE_ABT
bl z_arm_fault_prefetch
exception_exit
b z_arm_exc_exit
#if defined(CONFIG_FPU_SHARING)
#define FPU_SF_SIZE ___fpu_t_SIZEOF
#else
#define FPU_SF_SIZE 0
#endif
/**
* @brief Data abort exception handler
*
* A data abort (DABT) exception is generated when an error occurs on a data
* memory access. This exception can be either synchronous or asynchronous,
* depending on the type of fault that caused it.
*/
SECTION_SUBSEC_FUNC(TEXT, __exc, z_arm_data_abort)
/*
* The faulting instruction address is always offset by 8 for the data
* abort exceptions.
*/
sub lr, #8
exception_entry MODE_ABT
bl z_arm_fault_data
/*
* If z_arm_fault_data returns false, then we recovered from
* the error. It may have updated $pc, so copy $pc back to
* the true esf from the one passed to z_arm_fault_data.
*/
cmp r0, #0
ldreq r1, [sp, #24 + FPU_SF_SIZE]
exception_exit
streq r1, [sp, #24 + FPU_SF_SIZE]
b z_arm_exc_exit