aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorDimitris Papastamos <dimitris.papastamos@arm.com>2018-05-15 15:34:20 +0100
committerGitHub <noreply@github.com>2018-05-15 15:34:20 +0100
commita513506b0719a7321d755facb81f46e6272cfa90 (patch)
treeb0dd91d996616d6150396aa6626c5d1556636574 /lib
parent83cf7a006eead324f458fc961cb4bcc01b526c03 (diff)
parent0b9ce9064c9e27184ff1052d47c4fd0d9a07d521 (diff)
downloadplatform_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.S42
-rw-r--r--lib/el3_runtime/aarch64/context_mgmt.c5
-rw-r--r--lib/extensions/ras/ras_common.c138
-rw-r--r--lib/extensions/ras/std_err_record.c76
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;
+}