提交 2dec28bf 编写于 作者: S Shiju Jose 提交者: Yang Yingliang

ACPI / APEI: Add a notifier chain for unknown (vendor) CPER records

mainline inclusion
from mainline-v5.10-rc1
commit 9aa9cf3e
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I4CMAR
CVE: NA

--------------------------------

CPER records describing a firmware-first error are identified by GUID.
The ghes driver currently logs, but ignores any unknown CPER records.
This prevents describing errors that can't be represented by a standard
entry, that would otherwise allow a driver to recover from an error.
The UEFI spec calls these 'Non-standard Section Body' (N.2.3 of
version 2.8).

Add a notifier chain for these non-standard/vendor-records. Callers
must identify their type of records by GUID.

Record data is copied to memory from the ghes_estatus_pool to allow
us to keep it until after the notifier has run.
Co-developed-by: NJames Morse <james.morse@arm.com>
Link: https://lore.kernel.org/r/20200903123456.1823-2-shiju.jose@huawei.comSigned-off-by: NJames Morse <james.morse@arm.com>
Signed-off-by: NShiju Jose <shiju.jose@huawei.com>
Signed-off-by: NLorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Acked-by: N"Rafael J. Wysocki" <rjw@rjwysocki.net>
Signed-off-by: NWeilong Chen <chenweilong@huawei.com>
Reviewed-by: NXie XiuQi <xiexiuqi@huawei.com>
Signed-off-by: NYang Yingliang <yangyingliang@huawei.com>
上级 12d9b3be
...@@ -84,6 +84,12 @@ ...@@ -84,6 +84,12 @@
((struct acpi_hest_generic_status *) \ ((struct acpi_hest_generic_status *) \
((struct ghes_estatus_node *)(estatus_node) + 1)) ((struct ghes_estatus_node *)(estatus_node) + 1))
#define GHES_VENDOR_ENTRY_LEN(gdata_len) \
(sizeof(struct ghes_vendor_record_entry) + (gdata_len))
#define GHES_GDATA_FROM_VENDOR_ENTRY(vendor_entry) \
((struct acpi_hest_generic_data *) \
((struct ghes_vendor_record_entry *)(vendor_entry) + 1))
static inline bool is_hest_type_generic_v2(struct ghes *ghes) static inline bool is_hest_type_generic_v2(struct ghes *ghes)
{ {
return ghes->generic->header.type == ACPI_HEST_TYPE_GENERIC_ERROR_V2; return ghes->generic->header.type == ACPI_HEST_TYPE_GENERIC_ERROR_V2;
...@@ -126,6 +132,12 @@ EXPORT_SYMBOL(ghes_ts_err_chain); ...@@ -126,6 +132,12 @@ EXPORT_SYMBOL(ghes_ts_err_chain);
static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi);
static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); static DEFINE_SPINLOCK(ghes_ioremap_lock_irq);
struct ghes_vendor_record_entry {
struct work_struct work;
int error_severity;
char vendor_record[];
};
static struct gen_pool *ghes_estatus_pool; static struct gen_pool *ghes_estatus_pool;
static unsigned long ghes_estatus_pool_size_request; static unsigned long ghes_estatus_pool_size_request;
...@@ -464,6 +476,57 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata) ...@@ -464,6 +476,57 @@ static void ghes_handle_aer(struct acpi_hest_generic_data *gdata)
#endif #endif
} }
static BLOCKING_NOTIFIER_HEAD(vendor_record_notify_list);
int ghes_register_vendor_record_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&vendor_record_notify_list, nb);
}
EXPORT_SYMBOL_GPL(ghes_register_vendor_record_notifier);
void ghes_unregister_vendor_record_notifier(struct notifier_block *nb)
{
blocking_notifier_chain_unregister(&vendor_record_notify_list, nb);
}
EXPORT_SYMBOL_GPL(ghes_unregister_vendor_record_notifier);
static void ghes_vendor_record_work_func(struct work_struct *work)
{
struct ghes_vendor_record_entry *entry;
struct acpi_hest_generic_data *gdata;
u32 len;
entry = container_of(work, struct ghes_vendor_record_entry, work);
gdata = GHES_GDATA_FROM_VENDOR_ENTRY(entry);
blocking_notifier_call_chain(&vendor_record_notify_list,
entry->error_severity, gdata);
len = GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata));
gen_pool_free(ghes_estatus_pool, (unsigned long)entry, len);
}
static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata,
int sev)
{
struct acpi_hest_generic_data *copied_gdata;
struct ghes_vendor_record_entry *entry;
u32 len;
len = GHES_VENDOR_ENTRY_LEN(acpi_hest_get_record_size(gdata));
entry = (void *)gen_pool_alloc(ghes_estatus_pool, len);
if (!entry)
return;
copied_gdata = GHES_GDATA_FROM_VENDOR_ENTRY(entry);
memcpy(copied_gdata, gdata, acpi_hest_get_record_size(gdata));
entry->error_severity = sev;
INIT_WORK(&entry->work, ghes_vendor_record_work_func);
schedule_work(&entry->work);
}
void __weak ghes_arm_process_error(struct ghes *ghes, void __weak ghes_arm_process_error(struct ghes *ghes,
struct cper_sec_proc_arm *err, int sec_sev) struct cper_sec_proc_arm *err, int sec_sev)
{ {
...@@ -517,6 +580,7 @@ static void ghes_do_proc(struct ghes *ghes, ...@@ -517,6 +580,7 @@ static void ghes_do_proc(struct ghes *ghes,
} else { } else {
void *err = acpi_hest_get_payload(gdata); void *err = acpi_hest_get_payload(gdata);
ghes_defer_non_standard_event(gdata, sev);
log_non_standard_event(sec_type, fru_id, fru_text, log_non_standard_event(sec_type, fru_id, fru_text,
sec_sev, err, sec_sev, err,
gdata->error_data_length); gdata->error_data_length);
......
...@@ -52,6 +52,24 @@ enum { ...@@ -52,6 +52,24 @@ enum {
GHES_SEV_PANIC = 0x3, GHES_SEV_PANIC = 0x3,
}; };
#ifdef CONFIG_ACPI_APEI_GHES
/**
* ghes_register_vendor_record_notifier - register a notifier for vendor
* records that the kernel would otherwise ignore.
* @nb: pointer to the notifier_block structure of the event handler.
*
* return 0 : SUCCESS, non-zero : FAIL
*/
int ghes_register_vendor_record_notifier(struct notifier_block *nb);
/**
* ghes_unregister_vendor_record_notifier - unregister the previously
* registered vendor record notifier.
* @nb: pointer to the notifier_block structure of the vendor record handler.
*/
void ghes_unregister_vendor_record_notifier(struct notifier_block *nb);
#endif
/* From drivers/edac/ghes_edac.c */ /* From drivers/edac/ghes_edac.c */
#ifdef CONFIG_EDAC_GHES #ifdef CONFIG_EDAC_GHES
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册