summaryrefslogtreecommitdiffstats
path: root/lib/extensions/ras
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/extensions/ras/ras_common.c184
-rw-r--r--lib/extensions/ras/std_err_record.c79
2 files changed, 263 insertions, 0 deletions
diff --git a/lib/extensions/ras/ras_common.c b/lib/extensions/ras/ras_common.c
new file mode 100644
index 0000000..622879e
--- /dev/null
+++ b/lib/extensions/ras/ras_common.c
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2018-2021, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <stdbool.h>
+
+#include <arch_helpers.h>
+#include <bl31/ea_handle.h>
+#include <bl31/ehf.h>
+#include <common/debug.h>
+#include <lib/extensions/ras.h>
+#include <lib/extensions/ras_arch.h>
+#include <plat/common/platform.h>
+
+#ifndef PLAT_RAS_PRI
+# error Platform must define RAS priority value
+#endif
+
+/*
+ * Function to convert architecturally-defined primary error code SERR,
+ * bits[7:0] from ERR<n>STATUS to its corresponding error string.
+ */
+const char *ras_serr_to_str(unsigned int serr)
+{
+ const char *str[ERROR_STATUS_NUM_SERR] = {
+ "No error",
+ "IMPLEMENTATION DEFINED error",
+ "Data value from (non-associative) internal memory",
+ "IMPLEMENTATION DEFINED pin",
+ "Assertion failure",
+ "Error detected on internal data path",
+ "Data value from associative memory",
+ "Address/control value from associative memory",
+ "Data value from a TLB",
+ "Address/control value from a TLB",
+ "Data value from producer",
+ "Address/control value from producer",
+ "Data value from (non-associative) external memory",
+ "Illegal address (software fault)",
+ "Illegal access (software fault)",
+ "Illegal state (software fault)",
+ "Internal data register",
+ "Internal control register",
+ "Error response from slave",
+ "External timeout",
+ "Internal timeout",
+ "Deferred error from slave not supported at master"
+ };
+
+ /*
+ * All other values are reserved. Reserved values might be defined
+ * in a future version of the architecture
+ */
+ if (serr >= ERROR_STATUS_NUM_SERR)
+ return "unknown SERR";
+
+ return str[serr];
+}
+
+/* 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;
+ int probe_data, ret;
+ struct err_record_info *info;
+
+ const struct err_handler_data err_data = {
+ .version = ERR_HANDLER_VERSION,
+ .ea_reason = ea_reason,
+ .interrupt = 0,
+ .syndrome = (uint32_t) 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 (true) {
+ 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 != 0U) ? 1 : 0;
+}
+
+#if ENABLE_ASSERTIONS
+static void assert_interrupts_sorted(void)
+{
+ unsigned int i, last;
+ struct ras_interrupt *start = ras_interrupt_mappings.intrs;
+
+ if (ras_interrupt_mappings.num_intrs == 0UL)
+ return;
+
+ last = start[0].intr_number;
+ for (i = 1; i < ras_interrupt_mappings.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_mappings.intrs;
+ struct ras_interrupt *selected = NULL;
+ int probe_data = 0;
+ int start, end, mid, 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_mappings.num_intrs > 0UL);
+
+ start = 0;
+ end = (int)ras_interrupt_mappings.num_intrs - 1;
+ 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();
+ }
+
+ if (selected->err_record->probe != NULL) {
+ 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);
+ (void) selected->err_record->handler(selected->err_record, probe_data,
+ &err_data);
+
+ return 0;
+}
+
+void __init 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 0000000..c03fbbe
--- /dev/null
+++ b/lib/extensions/ras/std_err_record.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <lib/extensions/ras_arch.h>
+#include <lib/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)
+{
+ unsigned int num_records, num_group_regs, i;
+ uint64_t gsr;
+
+ assert(base != 0UL);
+
+ /* Only 4K supported for now */
+ assert(size_num_k == STD_ERR_NODE_SIZE_NUM_K);
+
+ num_records = (unsigned int)
+ (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 >> 6U) + 1U;
+
+ /* 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 == 0ULL)
+ continue;
+
+ /* Return the index of the record in error */
+ if (probe_data != NULL)
+ *probe_data = (((int) (i << 6U)) + __builtin_ctzll(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)
+{
+ unsigned int i;
+ uint64_t status;
+ unsigned int max_idx __unused =
+ ((unsigned int) read_erridr_el1()) & ERRIDR_MASK;
+
+ assert(idx_start < max_idx);
+ assert(check_u32_overflow(idx_start, num_idx) == 0);
+ assert((idx_start + num_idx - 1U) < 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) != 0U) {
+ if (probe_data != NULL)
+ *probe_data = (int) i;
+ return 1;
+ }
+ }
+
+ return 0;
+}