RAS: Add helpers to access Standard Error Records
The ARMv8 RAS Extensions introduced Standard Error Records which are a set of standard registers through which: - Platform can configure RAS node policy; e.g., notification mechanism; - RAS nodes can record and expose error information for error handling agents. Standard Error Records can either be accessed via. memory-mapped or System registers. This patch adds helper functions to access registers and fields within an error record. Change-Id: I6594ba799f4a1789d7b1e45b3e17fd40e7e0ba5c Signed-off-by: Jeenu Viswambharan <jeenu.viswambharan@arm.com>
This commit is contained in:
parent
14c6016ad5
commit
30d81c36da
|
@ -717,4 +717,17 @@
|
|||
#define DISR_EL1 S3_0_C12_C1_1
|
||||
#define DISR_A_BIT 31
|
||||
|
||||
#define ERRIDR_EL1 S3_0_C5_C3_0
|
||||
#define ERRIDR_MASK 0xffff
|
||||
|
||||
#define ERRSELR_EL1 S3_0_C5_C3_1
|
||||
|
||||
/* System register access to Standard Error Record registers */
|
||||
#define ERXFR_EL1 S3_0_C5_C4_0
|
||||
#define ERXCTLR_EL1 S3_0_C5_C4_1
|
||||
#define ERXSTATUS_EL1 S3_0_C5_C4_2
|
||||
#define ERXADDR_EL1 S3_0_C5_C4_3
|
||||
#define ERXMISC0_EL1 S3_0_C5_C4_4
|
||||
#define ERXMISC1_EL1 S3_0_C5_C4_5
|
||||
|
||||
#endif /* __ARCH_H__ */
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
@ -333,6 +333,16 @@ DEFINE_RENAME_SYSREG_RW_FUNCS(pmblimitr_el1, PMBLIMITR_EL1)
|
|||
DEFINE_RENAME_SYSREG_WRITE_FUNC(zcr_el3, ZCR_EL3)
|
||||
DEFINE_RENAME_SYSREG_WRITE_FUNC(zcr_el2, ZCR_EL2)
|
||||
|
||||
DEFINE_RENAME_SYSREG_READ_FUNC(erridr_el1, ERRIDR_EL1)
|
||||
DEFINE_RENAME_SYSREG_WRITE_FUNC(errselr_el1, ERRSELR_EL1)
|
||||
|
||||
DEFINE_RENAME_SYSREG_READ_FUNC(erxfr_el1, ERXFR_EL1)
|
||||
DEFINE_RENAME_SYSREG_RW_FUNCS(erxctlr_el1, ERXCTLR_EL1)
|
||||
DEFINE_RENAME_SYSREG_RW_FUNCS(erxstatus_el1, ERXSTATUS_EL1)
|
||||
DEFINE_RENAME_SYSREG_READ_FUNC(erxaddr_el1, ERXADDR_EL1)
|
||||
DEFINE_RENAME_SYSREG_READ_FUNC(erxmisc0_el1, ERXMISC0_EL1)
|
||||
DEFINE_RENAME_SYSREG_READ_FUNC(erxmisc1_el1, ERXMISC1_EL1)
|
||||
|
||||
#define IS_IN_EL(x) \
|
||||
(GET_EL(read_CurrentEl()) == MODE_EL##x)
|
||||
|
||||
|
|
|
@ -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__ */
|
|
@ -67,6 +67,13 @@
|
|||
#define check_uptr_overflow(ptr, inc) \
|
||||
(((ptr) > UINTPTR_MAX - (inc)) ? 1 : 0)
|
||||
|
||||
/*
|
||||
* Evaluates to 1 if (u32 + inc) overflows, 0 otherwise.
|
||||
* Both arguments must be 32-bit unsigned integers (i.e. effectively uint32_t).
|
||||
*/
|
||||
#define check_u32_overflow(u32, inc) \
|
||||
((u32) > (UINT32_MAX - (inc)) ? 1 : 0)
|
||||
|
||||
/*
|
||||
* For those constants to be shared between C and other sources, apply a 'u'
|
||||
* or 'ull' suffix to the argument only in C, to avoid undefined or unintended
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue