diff --git a/include/lib/extensions/ras.h b/include/lib/extensions/ras.h new file mode 100644 index 000000000..c1ce19f62 --- /dev/null +++ b/include/lib/extensions/ras.h @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __RAS_COMMON__ +#define __RAS_COMMON__ + +#define ERR_HANDLER_VERSION 1 + +/* Error record access mechanism */ +#define ERR_ACCESS_SYSREG 0 +#define ERR_ACCESS_MEMMAP 1 + +/* + * Register all error records on the platform. + * + * This macro must be used in the same file as the array of error record info + * are declared. Only then would ARRAY_SIZE() yield a meaningful value. + */ +#define REGISTER_ERR_RECORD_INFO(_records) \ + const struct err_record_mapping err_record_mapping = { \ + .err_records = _records, \ + .num_err_records = ARRAY_SIZE(_records), \ + } + +/* Error record info iterator */ +#define for_each_err_record_info(_i, _info) \ + for (_i = 0, _info = err_record_mapping.err_records; \ + _i < err_record_mapping.num_err_records; \ + _i++, _info++) + +#define _ERR_RECORD_COMMON(_probe, _handler, _aux) \ + .probe = _probe, \ + .handler = _handler, \ + .aux_data = _aux, + +#define ERR_RECORD_SYSREG_V1(_idx_start, _num_idx, _probe, _handler, _aux) \ + { \ + .version = 1, \ + .sysreg.idx_start = _idx_start, \ + .sysreg.num_idx = _num_idx, \ + .access = ERR_ACCESS_SYSREG, \ + _ERR_RECORD_COMMON(_probe, _handler, _aux) \ + } + +#define ERR_RECORD_MEMMAP_V1(_base_addr, _size_num_k, _probe, _handler, _aux) \ + { \ + .version = 1, \ + .memmap.base_addr = _base_addr, \ + .memmap.size_num_k = _size_num_k, \ + .access = ERR_ACCESS_MEMMAP, \ + _ERR_RECORD_COMMON(_probe, _handler, _aux) \ + } + +#ifndef __ASSEMBLY__ + +#include +#include + +struct err_record_info; + +/* Function to probe a error record group for error */ +typedef int (*err_record_probe_t)(const struct err_record_info *info, + int *probe_data); + +/* Data passed to error record group handler */ +struct err_handler_data { + /* Info passed on from top-level exception handler */ + uint64_t flags; + void *cookie; + void *handle; + + /* Data structure version */ + unsigned int version; + + /* Reason for EA: one the ERROR_* constants */ + unsigned int ea_reason; + + /* + * For EAs received at vector, the value read from ESR; for an EA + * synchronized by ESB, the value of DISR. + */ + uint32_t syndrome; +}; + +/* Function to handle error from an error record group */ +typedef int (*err_record_handler_t)(const struct err_record_info *info, + int probe_data, const struct err_handler_data *const data); + +/* Error record information */ +struct err_record_info { + /* Function to probe error record group for errors */ + err_record_probe_t probe; + + /* Function to handle error record group errors */ + err_record_handler_t handler; + + /* Opaque group-specific data */ + void *aux_data; + + /* Additional information for Standard Error Records */ + union { + struct { + /* + * For a group accessed via. memory-mapped register, + * base address of the page hosting error records, and + * the size of the record group. + */ + uintptr_t base_addr; + + /* Size of group in number of KBs */ + unsigned int size_num_k; + } memmap; + + struct { + /* + * For error records accessed via. system register, index of + * the error record. + */ + unsigned int idx_start; + unsigned int num_idx; + } sysreg; + }; + + /* Data structure version */ + unsigned int version; + + /* Error record access mechanism */ + unsigned int access:1; +}; + +struct err_record_mapping { + struct err_record_info *err_records; + size_t num_err_records; +}; + +extern const struct err_record_mapping err_record_mapping; + + +/* + * Helper functions to probe memory-mapped and system registers implemented in + * Standard Error Record format + */ +static inline int ras_err_ser_probe_memmap(const struct err_record_info *info, + int *probe_data) +{ + assert(info->version == ERR_HANDLER_VERSION); + + return ser_probe_memmap(info->memmap.base_addr, info->memmap.size_num_k, + probe_data); +} + +static inline int ras_err_ser_probe_sysreg(const struct err_record_info *info, + int *probe_data) +{ + assert(info->version == ERR_HANDLER_VERSION); + + return ser_probe_sysreg(info->sysreg.idx_start, info->sysreg.num_idx, + probe_data); +} + +int ras_ea_handler(unsigned int ea_reason, uint64_t syndrome, void *cookie, + void *handle, uint64_t flags); + +#endif /* __ASSEMBLY__ */ +#endif /* __RAS_COMMON__ */ diff --git a/lib/extensions/ras/ras_common.c b/lib/extensions/ras/ras_common.c new file mode 100644 index 000000000..2e316edae --- /dev/null +++ b/lib/extensions/ras/ras_common.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +/* 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, + .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); +} diff --git a/plat/common/aarch64/plat_common.c b/plat/common/aarch64/plat_common.c index c4531565b..5c240a8ea 100644 --- a/plat/common/aarch64/plat_common.c +++ b/plat/common/aarch64/plat_common.c @@ -8,6 +8,9 @@ #include #include #include +#if RAS_EXTENSION +#include +#endif #include /* @@ -112,6 +115,13 @@ int plat_sdei_validate_entry_point(uintptr_t ep, unsigned int client_mode) void plat_ea_handler(unsigned int ea_reason, uint64_t syndrome, void *cookie, void *handle, uint64_t flags) { +#if RAS_EXTENSION + /* Call RAS EA handler */ + int handled = ras_ea_handler(ea_reason, syndrome, cookie, handle, flags); + if (handled != 0) + return; +#endif + ERROR("Unhandled External Abort received on 0x%lx at EL3!\n", read_mpidr_el1()); ERROR(" exception reason=%u syndrome=0x%lx\n", ea_reason, syndrome);