Merge pull request #1485 from jeenu-arm/ras

Double-fault and fatal error handling support
This commit is contained in:
danh-arm 2018-07-24 14:36:43 +01:00 committed by GitHub
commit 790e6c5b96
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 452 additions and 126 deletions

263
bl31/aarch64/ea_delegate.S Normal file
View File

@ -0,0 +1,263 @@
/*
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert_macros.S>
#include <asm_macros.S>
#include <assert_macros.S>
#include <context.h>
#include <ea_handle.h>
#include <ras_arch.h>
.globl handle_lower_el_ea_esb
.globl enter_lower_el_sync_ea
.globl enter_lower_el_async_ea
/*
* Function to delegate External Aborts synchronized by ESB instruction at EL3
* vector entry. This function assumes GP registers x0-x29 have been saved, and
* are available for use. It delegates the handling of the EA to platform
* handler, and returns only upon successfully handling the EA; otherwise
* panics. On return from this function, the original exception handler is
* expected to resume.
*/
func handle_lower_el_ea_esb
mov x0, #ERROR_EA_ESB
mrs x1, DISR_EL1
b ea_proceed
endfunc handle_lower_el_ea_esb
/*
* This function forms the tail end of Synchronous Exception entry from lower
* EL, and expects to handle only Synchronous External Aborts from lower EL. If
* any other kind of exception is detected, then this function reports unhandled
* exception.
*
* Since it's part of exception vector, this function doesn't expect any GP
* registers to have been saved. It delegates the handling of the EA to platform
* handler, and upon successfully handling the EA, exits EL3; otherwise panics.
*/
func enter_lower_el_sync_ea
/*
* Explicitly save x30 so as to free up a register and to enable
* branching.
*/
str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
mrs x30, esr_el3
ubfx x30, x30, #ESR_EC_SHIFT, #ESR_EC_LENGTH
/* Check for I/D aborts from lower EL */
cmp x30, #EC_IABORT_LOWER_EL
b.eq 1f
cmp x30, #EC_DABORT_LOWER_EL
b.ne 2f
1:
/* Test for EA bit in the instruction syndrome */
mrs x30, esr_el3
tbz x30, #ESR_ISS_EABORT_EA_BIT, 2f
/* Save GP registers */
bl save_gp_registers
/* Setup exception class and syndrome arguments for platform handler */
mov x0, #ERROR_EA_SYNC
mrs x1, esr_el3
adr x30, el3_exit
b delegate_sync_ea
2:
/* Synchronous exceptions other than the above are assumed to be EA */
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
no_ret report_unhandled_exception
endfunc enter_lower_el_sync_ea
/*
* This function handles SErrors from lower ELs.
*
* Since it's part of exception vector, this function doesn't expect any GP
* registers to have been saved. It delegates the handling of the EA to platform
* handler, and upon successfully handling the EA, exits EL3; otherwise panics.
*/
func enter_lower_el_async_ea
/*
* Explicitly save x30 so as to free up a register and to enable
* branching
*/
str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
/* Save GP registers */
bl save_gp_registers
/* Setup exception class and syndrome arguments for platform handler */
mov x0, #ERROR_EA_ASYNC
mrs x1, esr_el3
adr x30, el3_exit
b delegate_async_ea
endfunc enter_lower_el_async_ea
/*
* Prelude for Synchronous External Abort handling. This function assumes that
* all GP registers have been saved by the caller.
*
* x0: EA reason
* x1: EA syndrome
*/
func delegate_sync_ea
#if RAS_EXTENSION
/*
* Check for Uncontainable error type. If so, route to the platform
* fatal error handler rather than the generic EA one.
*/
ubfx x2, x1, #EABORT_SET_SHIFT, #EABORT_SET_WIDTH
cmp x2, #ERROR_STATUS_SET_UC
b.ne 1f
/* Check fault status code */
ubfx x3, x1, #EABORT_DFSC_SHIFT, #EABORT_DFSC_WIDTH
cmp x3, #SYNC_EA_FSC
b.ne 1f
no_ret plat_handle_uncontainable_ea
1:
#endif
b ea_proceed
endfunc delegate_sync_ea
/*
* Prelude for Asynchronous External Abort handling. This function assumes that
* all GP registers have been saved by the caller.
*
* x0: EA reason
* x1: EA syndrome
*/
func delegate_async_ea
#if RAS_EXTENSION
/*
* Check for Implementation Defined Syndrome. If so, skip checking
* Uncontainable error type from the syndrome as the format is unknown.
*/
tbnz x1, #SERROR_IDS_BIT, 1f
/*
* Check for Uncontainable error type. If so, route to the platform
* fatal error handler rather than the generic EA one.
*/
ubfx x2, x1, #EABORT_AET_SHIFT, #EABORT_AET_WIDTH
cmp x2, #ERROR_STATUS_UET_UC
b.ne 1f
/* Check DFSC for SError type */
ubfx x3, x1, #EABORT_DFSC_SHIFT, #EABORT_DFSC_WIDTH
cmp x3, #DFSC_SERROR
b.ne 1f
no_ret plat_handle_uncontainable_ea
1:
#endif
b ea_proceed
endfunc delegate_async_ea
/*
* Delegate External Abort handling to platform's EA handler. This function
* assumes that all GP registers have been saved by the caller.
*
* x0: EA reason
* x1: EA syndrome
*/
func ea_proceed
/*
* If the ESR loaded earlier is not zero, we were processing an EA
* already, and this is a double fault.
*/
ldr x5, [sp, #CTX_EL3STATE_OFFSET + CTX_ESR_EL3]
cbz x5, 1f
no_ret plat_handle_double_fault
1:
/* Save EL3 state */
mrs x2, spsr_el3
mrs x3, elr_el3
stp x2, x3, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
/*
* Save ESR as handling might involve lower ELs, and returning back to
* EL3 from there would trample the original ESR.
*/
mrs x4, scr_el3
mrs x5, esr_el3
stp x4, x5, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
/*
* Setup rest of arguments, and call platform External Abort handler.
*
* x0: EA reason (already in place)
* x1: Exception syndrome (already in place).
* x2: Cookie (unused for now).
* x3: Context pointer.
* x4: Flags (security state from SCR for now).
*/
mov x2, xzr
mov x3, sp
ubfx x4, x4, #0, #1
/* Switch to runtime stack */
ldr x5, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
msr spsel, #0
mov sp, x5
mov x29, x30
#if ENABLE_ASSERTIONS
/* Stash the stack pointer */
mov x28, sp
#endif
bl plat_ea_handler
#if ENABLE_ASSERTIONS
/*
* Error handling flows might involve long jumps; so upon returning from
* the platform error handler, validate that the we've completely
* unwound the stack.
*/
mov x27, sp
cmp x28, x27
ASM_ASSERT(eq)
#endif
/* Make SP point to context */
msr spsel, #1
/* Restore EL3 state and ESR */
ldp x1, x2, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
msr spsr_el3, x1
msr elr_el3, x2
/* Restore ESR_EL3 and SCR_EL3 */
ldp x3, x4, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
msr scr_el3, x3
msr esr_el3, x4
#if ENABLE_ASSERTIONS
cmp x4, xzr
ASM_ASSERT(ne)
#endif
/* Clear ESR storage */
str xzr, [sp, #CTX_EL3STATE_OFFSET + CTX_ESR_EL3]
ret x29
endfunc ea_proceed

View File

@ -66,9 +66,7 @@
/* Save GP registers and restore them afterwards */
bl save_gp_registers
mov x0, #ERROR_EA_ESB
mrs x1, DISR_EL1
bl delegate_ea
bl handle_lower_el_ea_esb
bl restore_gp_registers
1:
@ -80,27 +78,6 @@
#endif
.endm
/*
* Handle External Abort by delegating to the platform's EA handler.
* Once the platform handler returns, the macro exits EL3 and returns to
* where the abort was taken from.
*
* This macro assumes that x30 is available for use.
*
* 'abort_type' is a constant passed to the platform handler, indicating
* the cause of the External Abort.
*/
.macro handle_ea abort_type
/* Save GP registers */
bl save_gp_registers
/* Setup exception class and syndrome arguments for platform handler */
mov x0, \abort_type
mrs x1, esr_el3
adr x30, el3_exit
b delegate_ea
.endm
/* ---------------------------------------------------------------------
* This macro handles Synchronous exceptions.
* Only SMC exceptions are supported.
@ -130,23 +107,9 @@
cmp x30, #EC_AARCH64_SMC
b.eq smc_handler64
/* Check for I/D aborts from lower EL */
cmp x30, #EC_IABORT_LOWER_EL
b.eq 1f
cmp x30, #EC_DABORT_LOWER_EL
b.ne 2f
1:
/* Test for EA bit in the instruction syndrome */
mrs x30, esr_el3
tbz x30, #ESR_ISS_EABORT_EA_BIT, 2f
handle_ea #ERROR_EA_SYNC
2:
/* Other kinds of synchronous exceptions are not handled */
/* Synchronous exceptions other than the above are assumed to be EA */
ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
b report_unhandled_exception
b enter_lower_el_sync_ea
.endm
@ -250,7 +213,7 @@ end_vector_entry fiq_sp_el0
vector_entry serror_sp_el0
b report_unhandled_exception
no_ret plat_handle_el3_ea
end_vector_entry serror_sp_el0
/* ---------------------------------------------------------------------
@ -276,7 +239,7 @@ vector_entry fiq_sp_elx
end_vector_entry fiq_sp_elx
vector_entry serror_sp_elx
b report_unhandled_exception
no_ret plat_handle_el3_ea
end_vector_entry serror_sp_elx
/* ---------------------------------------------------------------------
@ -306,13 +269,7 @@ end_vector_entry fiq_aarch64
vector_entry serror_aarch64
msr daifclr, #DAIF_ABT_BIT
/*
* Explicitly save x30 so as to free up a register and to enable
* branching
*/
str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
handle_ea #ERROR_EA_ASYNC
b enter_lower_el_async_ea
end_vector_entry serror_aarch64
/* ---------------------------------------------------------------------
@ -342,13 +299,7 @@ end_vector_entry fiq_aarch32
vector_entry serror_aarch32
msr daifclr, #DAIF_ABT_BIT
/*
* Explicitly save x30 so as to free up a register and to enable
* branching
*/
str x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR]
handle_ea #ERROR_EA_ASYNC
b enter_lower_el_async_ea
end_vector_entry serror_aarch32
@ -525,62 +476,3 @@ rt_svc_fw_critical_error:
msr spsel, #1
no_ret report_unhandled_exception
endfunc smc_handler
/*
* Delegate External Abort handling to platform's EA handler. This function
* assumes that all GP registers have been saved by the caller.
*
* x0: EA reason
* x1: EA syndrome
*/
func delegate_ea
/* Save EL3 state */
mrs x2, spsr_el3
mrs x3, elr_el3
stp x2, x3, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
/*
* Save ESR as handling might involve lower ELs, and returning back to
* EL3 from there would trample the original ESR.
*/
mrs x4, scr_el3
mrs x5, esr_el3
stp x4, x5, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
/*
* Setup rest of arguments, and call platform External Abort handler.
*
* x0: EA reason (already in place)
* x1: Exception syndrome (already in place).
* x2: Cookie (unused for now).
* x3: Context pointer.
* x4: Flags (security state from SCR for now).
*/
mov x2, xzr
mov x3, sp
ubfx x4, x4, #0, #1
/* Switch to runtime stack */
ldr x5, [sp, #CTX_EL3STATE_OFFSET + CTX_RUNTIME_SP]
msr spsel, #0
mov sp, x5
mov x29, x30
bl plat_ea_handler
mov x30, x29
/* Make SP point to context */
msr spsel, #1
/* Restore EL3 state */
ldp x1, x2, [sp, #CTX_EL3STATE_OFFSET + CTX_SPSR_EL3]
msr spsr_el3, x1
msr elr_el3, x2
/* Restore ESR_EL3 and SCR_EL3 */
ldp x3, x4, [sp, #CTX_EL3STATE_OFFSET + CTX_SCR_EL3]
msr scr_el3, x3
msr esr_el3, x4
ret
endfunc delegate_ea

View File

@ -19,6 +19,7 @@ BL31_SOURCES += bl31/bl31_main.c \
bl31/interrupt_mgmt.c \
bl31/aarch64/bl31_entrypoint.S \
bl31/aarch64/crash_reporting.S \
bl31/aarch64/ea_delegate.S \
bl31/aarch64/runtime_exceptions.S \
bl31/bl31_context_mgmt.c \
common/runtime_svc.c \

View File

@ -2850,6 +2850,106 @@ you can keep the default implementation here (which calls ``console_flush()``).
If you're trying to debug crashes in BL1, you can call the console_xx_core_flush
function exported by some console drivers from here.
Extternal Abort handling and RAS Support
----------------------------------------
Function : plat_ea_handler
~~~~~~~~~~~~~~~~~~~~~~~~~~
::
Argument : int
Argument : uint64_t
Argument : void *
Argument : void *
Argument : uint64_t
Return : void
This function is invoked by the RAS framework for the platform to handle an
External Abort received at EL3. The intention of the function is to attempt to
resolve the cause of External Abort and return; if that's not possible, to
initiate orderly shutdown of the system.
The first parameter (``int ea_reason``) indicates the reason for External Abort.
Its value is one of ``ERROR_EA_*`` constants defined in ``ea_handle.h``.
The second parameter (``uint64_t syndrome``) is the respective syndrome
presented to EL3 after having received the External Abort. Depending on the
nature of the abort (as can be inferred from the ``ea_reason`` parameter), this
can be the content of either ``ESR_EL3`` or ``DISR_EL1``.
The third parameter (``void *cookie``) is unused for now. The fourth parameter
(``void *handle``) is a pointer to the preempted context. The fifth parameter
(``uint64_t flags``) indicates the preempted security state. These parameters
are received from the top-level exception handler.
If ``RAS_EXTENSION`` is set to ``1``, the default implementation of this
function iterates through RAS handlers registered by the platform. If any of the
RAS handlers resolve the External Abort, no further action is taken.
If ``RAS_EXTENSION`` is set to ``0``, or if none of the platform RAS handlers
could resolve the External Abort, the default implementation prints an error
message, and panics.
Function : plat_handle_uncontainable_ea
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
Argument : int
Argument : uint64_t
Return : void
This function is invoked by the RAS framework when an External Abort of
Uncontainable type is received at EL3. Due to the critical nature of
Uncontainable errors, the intention of this function is to initiate orderly
shutdown of the system, and is not expected to return.
This function must be implemented in assembly.
The first and second parameters are the same as that of ``plat_ea_handler``.
The default implementation of this function calls
``report_unhandled_exception``.
Function : plat_handle_double_fault
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
Argument : int
Argument : uint64_t
Return : void
This function is invoked by the RAS framework when another External Abort is
received at EL3 while one is already being handled. I.e., a call to
``plat_ea_handler`` is outstanding. Due to its critical nature, the intention of
this function is to initiate orderly shutdown of the system, and is not expected
recover or return.
This function must be implemented in assembly.
The first and second parameters are the same as that of ``plat_ea_handler``.
The default implementation of this function calls
``report_unhandled_exception``.
Function : plat_handle_el3_ea
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
::
Return : void
This function is invoked when an External Abort is received while executing in
EL3. Due to its critical nature, the intention of this function is to initiate
orderly shutdown of the system, and is not expected recover or return.
This function must be implemented in assembly.
The default implementation of this function calls
``report_unhandled_exception``.
Build flags
-----------

View File

@ -4,15 +4,8 @@
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef __RAS_H__
#define __RAS_H__
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <context.h>
#include <mmio.h>
#include <stdint.h>
#ifndef RAS_ARCH_H
#define RAS_ARCH_H
/*
* Size of nodes implementing Standard Error Records - currently only 4k is
@ -146,12 +139,53 @@
#define ERR_CTLR_ENABLE_FIELD(_ctlr, _field) \
ERR_CTLR_SET_FIELD(_ctlr, _field, ERR_CTLR_ ##_field ##_MASK)
/* Uncorrected error types */
/* Uncorrected error types for Asynchronous exceptions */
#define ERROR_STATUS_UET_UC 0x0 /* Uncontainable */
#define ERROR_STATUS_UET_UEU 0x1 /* Unrecoverable */
#define ERROR_STATUS_UET_UEO 0x2 /* Restable */
#define ERROR_STATUS_UET_UER 0x3 /* Recoverable */
/* Error types for Synchronous exceptions */
#define ERROR_STATUS_SET_UER 0x0 /* Recoverable */
#define ERROR_STATUS_SET_UEO 0x1 /* Restable */
#define ERROR_STATUS_SET_UC 0x2 /* Uncontainable */
#define ERROR_STATUS_SET_CE 0x3 /* Corrected */
/* Implementation Defined Syndrome bit in ESR */
#define SERROR_IDS_BIT U(24)
/*
* Asynchronous Error Type in exception syndrome. The field has same values in
* both DISR_EL1 and ESR_EL3 for SError.
*/
#define EABORT_AET_SHIFT U(10)
#define EABORT_AET_WIDTH U(3)
#define EABORT_AET_MASK U(0x7)
/* DFSC field in Asynchronous exception syndrome */
#define EABORT_DFSC_SHIFT U(0)
#define EABORT_DFSC_WIDTH U(6)
#define EABORT_DFSC_MASK U(0x3f)
/* Synchronous Error Type in exception syndrome. */
#define EABORT_SET_SHIFT U(11)
#define EABORT_SET_WIDTH U(2)
#define EABORT_SET_MASK U(0x3)
/* DFSC code for SErrors */
#define DFSC_SERROR 0x11
/* I/DFSC code for synchronous external abort */
#define SYNC_EA_FSC 0x10
#ifndef __ASSEMBLY__
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <context.h>
#include <mmio.h>
#include <stdint.h>
/*
* Standard Error Record accessors for memory-mapped registers.
@ -221,5 +255,6 @@ static inline void ser_sys_select_record(unsigned int idx)
/* Library functions to probe Standard Error Record */
int ser_probe_memmap(uintptr_t base, unsigned int size_num_k, int *probe_data);
int ser_probe_sysreg(unsigned int idx_start, unsigned int num_idx, int *probe_data);
#endif /* __ASSEMBLY__ */
#endif /* __RAS_H__ */
#endif /* RAS_ARCH_H */

View File

@ -20,6 +20,10 @@
.weak bl31_plat_enable_mmu
.weak bl32_plat_enable_mmu
.weak plat_handle_uncontainable_ea
.weak plat_handle_double_fault
.weak plat_handle_el3_ea
#if !ENABLE_PLAT_COMPAT
.globl platform_get_core_pos
@ -186,3 +190,34 @@ endfunc bl31_plat_enable_mmu
func bl32_plat_enable_mmu
b enable_mmu_direct_el1
endfunc bl32_plat_enable_mmu
/* -----------------------------------------------------
* Platform handler for Uncontainable External Abort.
*
* x0: EA reason
* x1: EA syndrome
* -----------------------------------------------------
*/
func plat_handle_uncontainable_ea
b report_unhandled_exception
endfunc plat_handle_uncontainable_ea
/* -----------------------------------------------------
* Platform handler for Double Fault.
*
* x0: EA reason
* x1: EA syndrome
* -----------------------------------------------------
*/
func plat_handle_double_fault
b report_unhandled_exception
endfunc plat_handle_double_fault
/* -----------------------------------------------------
* Platform handler for EL3 External Abort.
* -----------------------------------------------------
*/
func plat_handle_el3_ea
b report_unhandled_exception
endfunc plat_handle_el3_ea