Dimitris Papastamos
7 years ago
committed by
GitHub
23 changed files with 1041 additions and 61 deletions
@ -0,0 +1,24 @@ |
|||
/*
|
|||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#ifndef __EA_HANDLE_H__ |
|||
#define __EA_HANDLE_H__ |
|||
|
|||
/* Constants indicating the reason for an External Abort */ |
|||
|
|||
/* External Abort received at SError vector */ |
|||
#define ERROR_EA_ASYNC 0 |
|||
|
|||
/* Synchronous External Abort received at Synchronous exception vector */ |
|||
#define ERROR_EA_SYNC 1 |
|||
|
|||
/* External Abort synchronized by ESB instruction */ |
|||
#define ERROR_EA_ESB 2 |
|||
|
|||
/* RAS event signalled as peripheral interrupt */ |
|||
#define ERROR_INTERRUPT 3 |
|||
|
|||
#endif /* __EA_HANDLE_H__ */ |
@ -0,0 +1,199 @@ |
|||
/*
|
|||
* 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) \ |
|||
} |
|||
|
|||
/*
|
|||
* Macro to be used to name and declare an array of RAS interrupts along with |
|||
* their handlers. |
|||
* |
|||
* This macro must be used in the same file as the array of interrupts are |
|||
* declared. Only then would ARRAY_SIZE() yield a meaningful value. Also, the |
|||
* array is expected to be sorted in the increasing order of interrupt number. |
|||
*/ |
|||
#define REGISTER_RAS_INTERRUPTS(_array) \ |
|||
const struct ras_interrupt_mapping ras_interrupt_mapping = { \ |
|||
.intrs = _array, \ |
|||
.num_intrs = ARRAY_SIZE(_array), \ |
|||
} |
|||
|
|||
#ifndef __ASSEMBLY__ |
|||
|
|||
#include <assert.h> |
|||
#include <ras_arch.h> |
|||
|
|||
struct err_record_info; |
|||
|
|||
struct ras_interrupt { |
|||
/* Interrupt number, and the associated error record info */ |
|||
unsigned int intr_number; |
|||
struct err_record_info *err_record; |
|||
void *cookie; |
|||
}; |
|||
|
|||
/* 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; |
|||
|
|||
/* For errors signalled via. interrupt, the raw interrupt ID; otherwise, 0. */ |
|||
unsigned int interrupt; |
|||
}; |
|||
|
|||
/* 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; |
|||
}; |
|||
|
|||
struct ras_interrupt_mapping { |
|||
struct ras_interrupt *intrs; |
|||
size_t num_intrs; |
|||
}; |
|||
|
|||
extern const struct err_record_mapping err_record_mapping; |
|||
extern const struct ras_interrupt_mapping ras_interrupt_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); |
|||
void ras_init(void); |
|||
|
|||
#endif /* __ASSEMBLY__ */ |
|||
#endif /* __RAS_COMMON__ */ |
@ -0,0 +1,225 @@ |
|||
/*
|
|||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* 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> |
|||
|
|||
/*
|
|||
* Size of nodes implementing Standard Error Records - currently only 4k is |
|||
* supported. |
|||
*/ |
|||
#define STD_ERR_NODE_SIZE_NUM_K 4 |
|||
|
|||
/*
|
|||
* Individual register offsets within an error record in Standard Error Record |
|||
* format when error records are accessed through memory-mapped registers. |
|||
*/ |
|||
#define ERR_FR(n) (0x0 + (64 * (n))) |
|||
#define ERR_CTLR(n) (0x8 + (64 * (n))) |
|||
#define ERR_STATUS(n) (0x10 + (64 * (n))) |
|||
#define ERR_ADDR(n) (0x18 + (64 * (n))) |
|||
#define ERR_MISC0(n) (0x20 + (64 * (n))) |
|||
#define ERR_MISC1(n) (0x28 + (64 * (n))) |
|||
|
|||
/* Group Status Register (ERR_STATUS) offset */ |
|||
#define ERR_GSR(base, size_num_k, n) \ |
|||
((base) + (0x380 * (size_num_k)) + (8 * (n))) |
|||
|
|||
/* Management register offsets */ |
|||
#define ERR_DEVID(base, size_num_k) \ |
|||
((base) + ((0x400 * (size_num_k)) - 0x100) + 0xc8) |
|||
|
|||
#define ERR_DEVID_MASK 0xffff |
|||
|
|||
/* Standard Error Record status register fields */ |
|||
#define ERR_STATUS_AV_SHIFT 31 |
|||
#define ERR_STATUS_AV_MASK U(0x1) |
|||
|
|||
#define ERR_STATUS_V_SHIFT 30 |
|||
#define ERR_STATUS_V_MASK U(0x1) |
|||
|
|||
#define ERR_STATUS_UE_SHIFT 29 |
|||
#define ERR_STATUS_UE_MASK U(0x1) |
|||
|
|||
#define ERR_STATUS_ER_SHIFT 28 |
|||
#define ERR_STATUS_ER_MASK U(0x1) |
|||
|
|||
#define ERR_STATUS_OF_SHIFT 27 |
|||
#define ERR_STATUS_OF_MASK U(0x1) |
|||
|
|||
#define ERR_STATUS_MV_SHIFT 26 |
|||
#define ERR_STATUS_MV_MASK U(0x1) |
|||
|
|||
#define ERR_STATUS_CE_SHIFT 24 |
|||
#define ERR_STATUS_CE_MASK U(0x3) |
|||
|
|||
#define ERR_STATUS_DE_SHIFT 23 |
|||
#define ERR_STATUS_DE_MASK U(0x1) |
|||
|
|||
#define ERR_STATUS_PN_SHIFT 22 |
|||
#define ERR_STATUS_PN_MASK U(0x1) |
|||
|
|||
#define ERR_STATUS_UET_SHIFT 20 |
|||
#define ERR_STATUS_UET_MASK U(0x3) |
|||
|
|||
#define ERR_STATUS_IERR_SHIFT 8 |
|||
#define ERR_STATUS_IERR_MASK U(0xff) |
|||
|
|||
#define ERR_STATUS_SERR_SHIFT 0 |
|||
#define ERR_STATUS_SERR_MASK U(0xff) |
|||
|
|||
#define ERR_STATUS_GET_FIELD(_status, _field) \ |
|||
(((_status) >> ERR_STATUS_ ##_field ##_SHIFT) & ERR_STATUS_ ##_field ##_MASK) |
|||
|
|||
#define ERR_STATUS_CLR_FIELD(_status, _field) \ |
|||
(_status) &= ~(ERR_STATUS_ ##_field ##_MASK << ERR_STATUS_ ##_field ##_SHIFT) |
|||
|
|||
#define ERR_STATUS_SET_FIELD(_status, _field, _value) \ |
|||
(_status) |= (((_value) & ERR_STATUS_ ##_field ##_MASK) << ERR_STATUS_ ##_field ##_SHIFT) |
|||
|
|||
#define ERR_STATUS_WRITE_FIELD(_status, _field, _value) do { \ |
|||
ERR_STATUS_CLR_FIELD(_status, _field, _value); \ |
|||
ERR_STATUS_SET_FIELD(_status, _field, _value); \ |
|||
} while (0) |
|||
|
|||
|
|||
/* Standard Error Record control register fields */ |
|||
#define ERR_CTLR_WDUI_SHIFT 11 |
|||
#define ERR_CTLR_WDUI_MASK 0x1 |
|||
|
|||
#define ERR_CTLR_RDUI_SHIFT 10 |
|||
#define ERR_CTLR_RDUI_MASK 0x1 |
|||
#define ERR_CTLR_DUI_SHIFT ERR_CTLR_RDUI_SHIFT |
|||
#define ERR_CTLR_DUI_MASK ERR_CTLR_RDUI_MASK |
|||
|
|||
#define ERR_CTLR_WCFI_SHIFT 9 |
|||
#define ERR_CTLR_WCFI_MASK 0x1 |
|||
|
|||
#define ERR_CTLR_RCFI_SHIFT 8 |
|||
#define ERR_CTLR_RCFI_MASK 0x1 |
|||
#define ERR_CTLR_CFI_SHIFT ERR_CTLR_RCFI_SHIFT |
|||
#define ERR_CTLR_CFI_MASK ERR_CTLR_RCFI_MASK |
|||
|
|||
#define ERR_CTLR_WUE_SHIFT 7 |
|||
#define ERR_CTLR_WUE_MASK 0x1 |
|||
|
|||
#define ERR_CTLR_WFI_SHIFT 6 |
|||
#define ERR_CTLR_WFI_MASK 0x1 |
|||
|
|||
#define ERR_CTLR_WUI_SHIFT 5 |
|||
#define ERR_CTLR_WUI_MASK 0x1 |
|||
|
|||
#define ERR_CTLR_RUE_SHIFT 4 |
|||
#define ERR_CTLR_RUE_MASK 0x1 |
|||
#define ERR_CTLR_UE_SHIFT ERR_CTLR_RUE_SHIFT |
|||
#define ERR_CTLR_UE_MASK ERR_CTLR_RUE_MASK |
|||
|
|||
#define ERR_CTLR_RFI_SHIFT 3 |
|||
#define ERR_CTLR_RFI_MASK 0x1 |
|||
#define ERR_CTLR_FI_SHIFT ERR_CTLR_RFI_SHIFT |
|||
#define ERR_CTLR_FI_MASK ERR_CTLR_RFI_MASK |
|||
|
|||
#define ERR_CTLR_RUI_SHIFT 2 |
|||
#define ERR_CTLR_RUI_MASK 0x1 |
|||
#define ERR_CTLR_UI_SHIFT ERR_CTLR_RUI_SHIFT |
|||
#define ERR_CTLR_UI_MASK ERR_CTLR_RUI_MASK |
|||
|
|||
#define ERR_CTLR_ED_SHIFT 0 |
|||
#define ERR_CTLR_ED_MASK 0x1 |
|||
|
|||
#define ERR_CTLR_CLR_FIELD(_ctlr, _field) \ |
|||
(_ctlr) &= ~(ERR_CTLR_ ##_field _MASK << ERR_CTLR_ ##_field ##_SHIFT) |
|||
|
|||
#define ERR_CTLR_SET_FIELD(_ctlr, _field, _value) \ |
|||
(_ctlr) |= (((_value) & ERR_CTLR_ ##_field ##_MASK) << ERR_CTLR_ ##_field ##_SHIFT) |
|||
|
|||
#define ERR_CTLR_ENABLE_FIELD(_ctlr, _field) \ |
|||
ERR_CTLR_SET_FIELD(_ctlr, _field, ERR_CTLR_ ##_field ##_MASK) |
|||
|
|||
/* Uncorrected error types */ |
|||
#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 */ |
|||
|
|||
|
|||
/*
|
|||
* Standard Error Record accessors for memory-mapped registers. |
|||
*/ |
|||
|
|||
static inline uint64_t ser_get_feature(uintptr_t base, unsigned int idx) |
|||
{ |
|||
return mmio_read_64(base + ERR_FR(idx)); |
|||
} |
|||
|
|||
static inline uint64_t ser_get_control(uintptr_t base, unsigned int idx) |
|||
{ |
|||
return mmio_read_64(base + ERR_CTLR(idx)); |
|||
} |
|||
|
|||
static inline uint64_t ser_get_status(uintptr_t base, unsigned int idx) |
|||
{ |
|||
return mmio_read_64(base + ERR_STATUS(idx)); |
|||
} |
|||
|
|||
/*
|
|||
* Error handling agent would write to the status register to clear an |
|||
* identified/handled error. Most fields in the status register are |
|||
* conditional write-one-to-clear. |
|||
* |
|||
* Typically, to clear the status, it suffices to write back the same value |
|||
* previously read. However, if there were new, higher-priority errors recorded |
|||
* on the node since status was last read, writing read value won't clear the |
|||
* status. Therefore, an error handling agent must wait on and verify the status |
|||
* has indeed been cleared. |
|||
*/ |
|||
static inline void ser_set_status(uintptr_t base, unsigned int idx, |
|||
uint64_t status) |
|||
{ |
|||
mmio_write_64(base + ERR_STATUS(idx), status); |
|||
} |
|||
|
|||
static inline uint64_t ser_get_addr(uintptr_t base, unsigned int idx) |
|||
{ |
|||
return mmio_read_64(base + ERR_ADDR(idx)); |
|||
} |
|||
|
|||
static inline uint64_t ser_get_misc0(uintptr_t base, unsigned int idx) |
|||
{ |
|||
return mmio_read_64(base + ERR_MISC0(idx)); |
|||
} |
|||
|
|||
static inline uint64_t ser_get_misc1(uintptr_t base, unsigned int idx) |
|||
{ |
|||
return mmio_read_64(base + ERR_MISC1(idx)); |
|||
} |
|||
|
|||
|
|||
/*
|
|||
* Standard Error Record helpers for System registers. |
|||
*/ |
|||
static inline void ser_sys_select_record(unsigned int idx) |
|||
{ |
|||
unsigned int max_idx __unused = read_erridr_el1() & ERRIDR_MASK; |
|||
|
|||
assert(idx < max_idx); |
|||
|
|||
write_errselr_el1(idx); |
|||
isb(); |
|||
} |
|||
|
|||
/* 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 /* __RAS_H__ */ |
@ -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); |
|||
} |
@ -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; |
|||
} |
@ -0,0 +1,16 @@ |
|||
/*
|
|||
* Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. |
|||
* |
|||
* SPDX-License-Identifier: BSD-3-Clause |
|||
*/ |
|||
|
|||
#include <ras.h> |
|||
|
|||
struct ras_interrupt arm_ras_interrupts[] = { |
|||
}; |
|||
|
|||
struct err_record_info arm_err_records[] = { |
|||
}; |
|||
|
|||
REGISTER_ERR_RECORD_INFO(arm_err_records); |
|||
REGISTER_RAS_INTERRUPTS(arm_ras_interrupts); |
Loading…
Reference in new issue