Merge branch 'cleanup/messages' of git://git.kernel.org/pub/scm/linux/kernel/git...
[firefly-linux-kernel-4.4.55.git] / drivers / acpi / acpi_extlog.c
index 185334114d71005e649f10fd04acfcd4b7bf14ef..b3842ffc19ba20a210bec8dd8dc5f56cf9119de9 100644 (file)
 #include <linux/cper.h>
 #include <linux/ratelimit.h>
 #include <linux/edac.h>
+#include <linux/ras.h>
 #include <asm/cpu.h>
 #include <asm/mce.h>
 
 #include "apei/apei-internal.h"
+#include <ras/ras_event.h>
 
 #define EXT_ELOG_ENTRY_MASK    GENMASK_ULL(51, 0) /* elog entry address mask */
 
@@ -69,11 +71,11 @@ static u32 l1_percpu_entry;
 #define ELOG_ENTRY_ADDR(phyaddr) \
        (phyaddr - elog_base + (u8 *)elog_addr)
 
-static struct acpi_generic_status *extlog_elog_entry_check(int cpu, int bank)
+static struct acpi_hest_generic_status *extlog_elog_entry_check(int cpu, int bank)
 {
        int idx;
        u64 data;
-       struct acpi_generic_status *estatus;
+       struct acpi_hest_generic_status *estatus;
 
        WARN_ON(cpu < 0);
        idx = ELOG_IDX(cpu, bank);
@@ -82,7 +84,7 @@ static struct acpi_generic_status *extlog_elog_entry_check(int cpu, int bank)
                return NULL;
 
        data &= EXT_ELOG_ENTRY_MASK;
-       estatus = (struct acpi_generic_status *)ELOG_ENTRY_ADDR(data);
+       estatus = (struct acpi_hest_generic_status *)ELOG_ENTRY_ADDR(data);
 
        /* if no valid data in elog entry, just return */
        if (estatus->block_status == 0)
@@ -92,7 +94,7 @@ static struct acpi_generic_status *extlog_elog_entry_check(int cpu, int bank)
 }
 
 static void __print_extlog_rcd(const char *pfx,
-                              struct acpi_generic_status *estatus, int cpu)
+                              struct acpi_hest_generic_status *estatus, int cpu)
 {
        static atomic_t seqno;
        unsigned int curr_seqno;
@@ -111,7 +113,7 @@ static void __print_extlog_rcd(const char *pfx,
 }
 
 static int print_extlog_rcd(const char *pfx,
-                           struct acpi_generic_status *estatus, int cpu)
+                           struct acpi_hest_generic_status *estatus, int cpu)
 {
        /* Not more than 2 messages every 5 seconds */
        static DEFINE_RATELIMIT_STATE(ratelimit_corrected, 5*HZ, 2);
@@ -137,8 +139,12 @@ static int extlog_print(struct notifier_block *nb, unsigned long val,
        struct mce *mce = (struct mce *)data;
        int     bank = mce->bank;
        int     cpu = mce->extcpu;
-       struct acpi_generic_status *estatus;
-       int rc;
+       struct acpi_hest_generic_status *estatus, *tmp;
+       struct acpi_hest_generic_data *gdata;
+       const uuid_le *fru_id = &NULL_UUID_LE;
+       char *fru_text = "";
+       uuid_le *sec_type;
+       static u32 err_seq;
 
        estatus = extlog_elog_entry_check(cpu, bank);
        if (estatus == NULL)
@@ -148,8 +154,29 @@ static int extlog_print(struct notifier_block *nb, unsigned long val,
        /* clear record status to enable BIOS to update it again */
        estatus->block_status = 0;
 
-       rc = print_extlog_rcd(NULL, (struct acpi_generic_status *)elog_buf, cpu);
+       tmp = (struct acpi_hest_generic_status *)elog_buf;
+
+       if (!ras_userspace_consumers()) {
+               print_extlog_rcd(NULL, tmp, cpu);
+               goto out;
+       }
+
+       /* log event via trace */
+       err_seq++;
+       gdata = (struct acpi_hest_generic_data *)(tmp + 1);
+       if (gdata->validation_bits & CPER_SEC_VALID_FRU_ID)
+               fru_id = (uuid_le *)gdata->fru_id;
+       if (gdata->validation_bits & CPER_SEC_VALID_FRU_TEXT)
+               fru_text = gdata->fru_text;
+       sec_type = (uuid_le *)gdata->section_type;
+       if (!uuid_le_cmp(*sec_type, CPER_SEC_PLATFORM_MEM)) {
+               struct cper_sec_mem_err *mem = (void *)(gdata + 1);
+               if (gdata->error_data_length >= sizeof(*mem))
+                       trace_extlog_mem_event(mem, err_seq, fru_id, fru_text,
+                                              (u8)gdata->error_severity);
+       }
 
+out:
        return NOTIFY_STOP;
 }
 
@@ -196,19 +223,16 @@ static int __init extlog_init(void)
        u64 cap;
        int rc;
 
+       rdmsrl(MSR_IA32_MCG_CAP, cap);
+
+       if (!(cap & MCG_ELOG_P) || !extlog_get_l1addr())
+               return -ENODEV;
+
        if (get_edac_report_status() == EDAC_REPORTING_FORCE) {
                pr_warn("Not loading eMCA, error reporting force-enabled through EDAC.\n");
                return -EPERM;
        }
 
-       rc = -ENODEV;
-       rdmsrl(MSR_IA32_MCG_CAP, cap);
-       if (!(cap & MCG_ELOG_P))
-               return rc;
-
-       if (!extlog_get_l1addr())
-               return rc;
-
        rc = -EINVAL;
        /* get L1 header to fetch necessary information */
        l1_hdr_size = sizeof(struct extlog_l1_head);