diff options
author | Dimitris Papastamos <dimitris.papastamos@arm.com> | 2018-05-15 15:34:20 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-05-15 15:34:20 +0100 |
commit | a513506b0719a7321d755facb81f46e6272cfa90 (patch) | |
tree | b0dd91d996616d6150396aa6626c5d1556636574 /lib | |
parent | 83cf7a006eead324f458fc961cb4bcc01b526c03 (diff) | |
parent | 0b9ce9064c9e27184ff1052d47c4fd0d9a07d521 (diff) | |
download | platform_external_arm-trusted-firmware-a513506b0719a7321d755facb81f46e6272cfa90.tar.gz platform_external_arm-trusted-firmware-a513506b0719a7321d755facb81f46e6272cfa90.tar.bz2 platform_external_arm-trusted-firmware-a513506b0719a7321d755facb81f46e6272cfa90.zip |
Merge pull request #1373 from jeenu-arm/ras-support
RAS support
Diffstat (limited to 'lib')
-rw-r--r-- | lib/el3_runtime/aarch64/context.S | 42 | ||||
-rw-r--r-- | lib/el3_runtime/aarch64/context_mgmt.c | 5 | ||||
-rw-r--r-- | lib/extensions/ras/ras_common.c | 138 | ||||
-rw-r--r-- | lib/extensions/ras/std_err_record.c | 76 |
4 files changed, 250 insertions, 11 deletions
diff --git a/lib/el3_runtime/aarch64/context.S b/lib/el3_runtime/aarch64/context.S index 620ec16ff..121ca4d30 100644 --- a/lib/el3_runtime/aarch64/context.S +++ b/lib/el3_runtime/aarch64/context.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -15,8 +15,8 @@ .global fpregs_context_restore #endif .global save_gp_registers + .global restore_gp_registers .global restore_gp_registers_eret - .global restore_gp_registers_callee_eret .global el3_exit /* ----------------------------------------------------- @@ -332,30 +332,50 @@ func save_gp_registers ret endfunc save_gp_registers -func restore_gp_registers_eret +/* + * This function restores all general purpose registers except x30 from the + * CPU context. x30 register must be explicitly restored by the caller. + */ +func restore_gp_registers ldp x0, x1, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X0] ldp x2, x3, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X2] - b restore_gp_registers_callee_eret -endfunc restore_gp_registers_eret - -func restore_gp_registers_callee_eret ldp x4, x5, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X4] ldp x6, x7, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X6] ldp x8, x9, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X8] ldp x10, x11, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X10] ldp x12, x13, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X12] ldp x14, x15, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X14] + ldp x16, x17, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X16] ldp x18, x19, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X18] ldp x20, x21, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X20] ldp x22, x23, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X22] ldp x24, x25, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X24] ldp x26, x27, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X26] + ldr x28, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_SP_EL0] + msr sp_el0, x28 ldp x28, x29, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X28] - ldp x30, x17, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR] - msr sp_el0, x17 - ldp x16, x17, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_X16] + ret +endfunc restore_gp_registers + +/* + * Restore general purpose registers (including x30), and exit EL3 via. ERET to + * a lower exception level. + */ +func restore_gp_registers_eret + bl restore_gp_registers + ldr x30, [sp, #CTX_GPREGS_OFFSET + CTX_GPREG_LR] + +#if IMAGE_BL31 && RAS_EXTENSION + /* + * Issue Error Synchronization Barrier to synchronize SErrors before + * exiting EL3. We're running with EAs unmasked, so any synchronized + * errors would be taken immediately; therefore no need to inspect + * DISR_EL1 register. + */ + esb +#endif eret -endfunc restore_gp_registers_callee_eret +endfunc restore_gp_registers_eret /* ----------------------------------------------------- * This routine assumes that the SP_EL3 is pointing to diff --git a/lib/el3_runtime/aarch64/context_mgmt.c b/lib/el3_runtime/aarch64/context_mgmt.c index 2608d1fcc..76eecc188 100644 --- a/lib/el3_runtime/aarch64/context_mgmt.c +++ b/lib/el3_runtime/aarch64/context_mgmt.c @@ -114,6 +114,11 @@ static void cm_init_context_common(cpu_context_t *ctx, const entry_point_info_t scr_el3 &= ~SCR_EA_BIT; #endif +#if FAULT_INJECTION_SUPPORT + /* Enable fault injection from lower ELs */ + scr_el3 |= SCR_FIEN_BIT; +#endif + #ifdef IMAGE_BL31 /* * SCR_EL3.IRQ, SCR_EL3.FIQ: Enable the physical FIQ and IRQ rounting as diff --git a/lib/extensions/ras/ras_common.c b/lib/extensions/ras/ras_common.c new file mode 100644 index 000000000..0335a7bcb --- /dev/null +++ b/lib/extensions/ras/ras_common.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch_helpers.h> +#include <debug.h> +#include <ea_handle.h> +#include <ehf.h> +#include <platform.h> +#include <ras.h> +#include <ras_arch.h> + +#ifndef PLAT_RAS_PRI +# error Platform must define RAS priority value +#endif + +/* Handler that receives External Aborts on RAS-capable systems */ +int ras_ea_handler(unsigned int ea_reason, uint64_t syndrome, void *cookie, + void *handle, uint64_t flags) +{ + unsigned int i, n_handled = 0, ret; + int probe_data; + struct err_record_info *info; + + const struct err_handler_data err_data = { + .version = ERR_HANDLER_VERSION, + .ea_reason = ea_reason, + .interrupt = 0, + .syndrome = syndrome, + .flags = flags, + .cookie = cookie, + .handle = handle + }; + + for_each_err_record_info(i, info) { + assert(info->probe != NULL); + assert(info->handler != NULL); + + /* Continue probing until the record group signals no error */ + while (1) { + if (info->probe(info, &probe_data) == 0) + break; + + /* Handle error */ + ret = info->handler(info, probe_data, &err_data); + if (ret != 0) + return ret; + + n_handled++; + } + } + + return (n_handled != 0); +} + +#if ENABLE_ASSERTIONS +static void assert_interrupts_sorted(void) +{ + unsigned int i, last; + struct ras_interrupt *start = ras_interrupt_mapping.intrs; + + if (ras_interrupt_mapping.num_intrs == 0) + return; + + last = start[0].intr_number; + for (i = 1; i < ras_interrupt_mapping.num_intrs; i++) { + assert(start[i].intr_number > last); + last = start[i].intr_number; + } +} +#endif + +/* + * Given an RAS interrupt number, locate the registered handler and call it. If + * no handler was found for the interrupt number, this function panics. + */ +static int ras_interrupt_handler(uint32_t intr_raw, uint32_t flags, + void *handle, void *cookie) +{ + struct ras_interrupt *ras_inrs = ras_interrupt_mapping.intrs; + struct ras_interrupt *selected = NULL; + int start, end, mid, probe_data, ret __unused; + + const struct err_handler_data err_data = { + .version = ERR_HANDLER_VERSION, + .interrupt = intr_raw, + .flags = flags, + .cookie = cookie, + .handle = handle + }; + + assert(ras_interrupt_mapping.num_intrs > 0); + + start = 0; + end = ras_interrupt_mapping.num_intrs; + while (start <= end) { + mid = ((end + start) / 2); + if (intr_raw == ras_inrs[mid].intr_number) { + selected = &ras_inrs[mid]; + break; + } else if (intr_raw < ras_inrs[mid].intr_number) { + /* Move left */ + end = mid - 1; + } else { + /* Move right */ + start = mid + 1; + } + } + + if (selected == NULL) { + ERROR("RAS interrupt %u has no handler!\n", intr_raw); + panic(); + } + + + ret = selected->err_record->probe(selected->err_record, &probe_data); + assert(ret != 0); + + /* Call error handler for the record group */ + assert(selected->err_record->handler != NULL); + selected->err_record->handler(selected->err_record, probe_data, + &err_data); + + return 0; +} + +void ras_init(void) +{ +#if ENABLE_ASSERTIONS + /* Check RAS interrupts are sorted */ + assert_interrupts_sorted(); +#endif + + /* Register RAS priority handler */ + ehf_register_priority_handler(PLAT_RAS_PRI, ras_interrupt_handler); +} diff --git a/lib/extensions/ras/std_err_record.c b/lib/extensions/ras/std_err_record.c new file mode 100644 index 000000000..65c007f9e --- /dev/null +++ b/lib/extensions/ras/std_err_record.c @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <ras_arch.h> +#include <utils_def.h> + +/* + * Probe for error in memory-mapped registers containing error records + * implemented Standard Error Record format. Upon detecting an error, set probe + * data to the index of the record in error, and return 1; otherwise, return 0. + */ +int ser_probe_memmap(uintptr_t base, unsigned int size_num_k, int *probe_data) +{ + int num_records, num_group_regs, i; + uint64_t gsr; + + assert(base != 0); + + /* Only 4K supported for now */ + assert(size_num_k == STD_ERR_NODE_SIZE_NUM_K); + + num_records = (mmio_read_32(ERR_DEVID(base, size_num_k)) & ERR_DEVID_MASK); + + /* A group register shows error status for 2^6 error records */ + num_group_regs = (num_records >> 6) + 1; + + /* Iterate through group registers to find a record in error */ + for (i = 0; i < num_group_regs; i++) { + gsr = mmio_read_64(ERR_GSR(base, size_num_k, i)); + if (gsr == 0) + continue; + + /* Return the index of the record in error */ + if (probe_data != NULL) + *probe_data = ((i << 6) + __builtin_ctz(gsr)); + + return 1; + } + + return 0; +} + +/* + * Probe for error in System Registers where error records are implemented in + * Standard Error Record format. Upon detecting an error, set probe data to the + * index of the record in error, and return 1; otherwise, return 0. + */ +int ser_probe_sysreg(unsigned int idx_start, unsigned int num_idx, int *probe_data) +{ + int i; + uint64_t status; + unsigned int max_idx __unused = read_erridr_el1() & ERRIDR_MASK; + + assert(idx_start < max_idx); + assert(check_u32_overflow(idx_start, num_idx) == 0); + assert((idx_start + num_idx - 1) < max_idx); + + for (i = 0; i < num_idx; i++) { + /* Select the error record */ + ser_sys_select_record(idx_start + i); + + /* Retrieve status register from the error record */ + status = read_erxstatus_el1(); + + /* Check for valid field in status */ + if (ERR_STATUS_GET_FIELD(status, V)) { + if (probe_data != NULL) + *probe_data = i; + return 1; + } + } + + return 0; +} |