d9e469
commit 38d48ed48f9d0baa20786d98abe2b4085fca7d5d
d9e469
Author: Luck, Tony <tony.luck@intel.com>
d9e469
Date:   Mon Aug 4 13:29:01 2014 -0700
d9e469
d9e469
    rasdaemon: Add support for extlog trace events
d9e469
    
d9e469
    Linux kernel 3.17 includes a new trace event to pick up extended
d9e469
    error logs produced by BIOS in the Common Platform Error Record
d9e469
    format described in appendix N of the UEFI standard. This patch
d9e469
    adds support to collect that information and log it both in
d9e469
    readable ASCII and into the sqlite3 database that rasdaemon
d9e469
    uses to store all error information.  In addition ras-mc-ctl
d9e469
    is updated to query that database for both detailed and summary
d9e469
    reports.
d9e469
    
d9e469
    Big thanks to Aristeu for pretty much all the sqlite3 pieces,
d9e469
    plus testing and fixing miscellaneous issues elsewhere.
d9e469
    
d9e469
    Signed-off-by: Tony Luck <tony.luck@intel.com>
d9e469
    Signed-off-by: Mauro Carvalho Chehab <m.chehab@samsung.com>
d9e469
d9e469
diff --git a/Makefile.am b/Makefile.am
d9e469
index 0fa615f..117c970 100644
d9e469
--- a/Makefile.am
d9e469
+++ b/Makefile.am
d9e469
@@ -30,13 +30,17 @@ if WITH_MCE
d9e469
 			mce-intel-dunnington.c mce-intel-tulsa.c \
d9e469
 			mce-intel-sb.c mce-intel-ivb.c
d9e469
 endif
d9e469
+if WITH_EXTLOG
d9e469
+   rasdaemon_SOURCES += ras-extlog-handler.c
d9e469
+endif
d9e469
 if WITH_ABRT_REPORT
d9e469
    rasdaemon_SOURCES += ras-report.c
d9e469
 endif
d9e469
 rasdaemon_LDADD = -lpthread $(SQLITE3_LIBS) libtrace/libtrace.a
d9e469
 
d9e469
 include_HEADERS = config.h  ras-events.h  ras-logger.h  ras-mc-handler.h \
d9e469
-		  ras-aer-handler.h ras-mce-handler.h ras-record.h bitfield.h ras-report.h
d9e469
+		  ras-aer-handler.h ras-mce-handler.h ras-record.h bitfield.h ras-report.h \
d9e469
+		  ras-extlog-handler.h
d9e469
 
d9e469
 # This rule can't be called with more than one Makefile job (like make -j8)
d9e469
 # I can't figure out a way to fix that
d9e469
diff --git a/configure.ac b/configure.ac
d9e469
index 64a5b13..9495491 100644
d9e469
--- a/configure.ac
d9e469
+++ b/configure.ac
d9e469
@@ -53,6 +53,15 @@ AS_IF([test "x$enable_mce" = "xyes"], [
d9e469
 ])
d9e469
 AM_CONDITIONAL([WITH_MCE], [test x$enable_mce = xyes])
d9e469
 
d9e469
+AC_ARG_ENABLE([extlog],
d9e469
+    AS_HELP_STRING([--enable-extlog], [enable EXTLOG events (currently experimental)]))
d9e469
+
d9e469
+AS_IF([test "x$enable_extlog" = "xyes"], [
d9e469
+  AC_DEFINE(HAVE_EXTLOG,1,"have EXTLOG events collect")
d9e469
+  AC_SUBST([WITH_EXTLOG])
d9e469
+])
d9e469
+AM_CONDITIONAL([WITH_EXTLOG], [test x$enable_extlog = xyes])
d9e469
+
d9e469
 AC_ARG_ENABLE([abrt_report],
d9e469
     AS_HELP_STRING([--enable-abrt-report], [enable report event to ABRT (currently experimental)]))
d9e469
 
d9e469
diff --git a/ras-aer-handler.c b/ras-aer-handler.c
d9e469
index 50526af..bb7c0b9 100644
d9e469
--- a/ras-aer-handler.c
d9e469
+++ b/ras-aer-handler.c
d9e469
@@ -70,7 +70,7 @@ int ras_aer_event_handler(struct trace_seq *s,
d9e469
 	 */
d9e469
 
d9e469
 	if (ras->use_uptime)
d9e469
-		now = record->ts/1000000000L + ras->uptime_diff;
d9e469
+		now = record->ts/user_hz + ras->uptime_diff;
d9e469
 	else
d9e469
 		now = time(NULL);
d9e469
 
d9e469
diff --git a/ras-events.c b/ras-events.c
d9e469
index ecbbd3a..0be7c3f 100644
d9e469
--- a/ras-events.c
d9e469
+++ b/ras-events.c
d9e469
@@ -30,6 +30,7 @@
d9e469
 #include "ras-mc-handler.h"
d9e469
 #include "ras-aer-handler.h"
d9e469
 #include "ras-mce-handler.h"
d9e469
+#include "ras-extlog-handler.h"
d9e469
 #include "ras-record.h"
d9e469
 #include "ras-logger.h"
d9e469
 
d9e469
@@ -203,6 +204,10 @@ int toggle_ras_mc_event(int enable)
d9e469
 	rc |= __toggle_ras_mc_event(ras, "mce", "mce_record", enable);
d9e469
 #endif
d9e469
 
d9e469
+#ifdef HAVE_EXTLOG
d9e469
+	rc |= __toggle_ras_mc_event(ras, "ras", "extlog_mem_event", enable);
d9e469
+#endif
d9e469
+
d9e469
 free_ras:
d9e469
 	free(ras);
d9e469
 	return rc;
d9e469
@@ -688,6 +693,19 @@ int handle_ras_events(int record_events)
d9e469
 		    "mce", "mce_record");
d9e469
 	}
d9e469
 #endif
d9e469
+
d9e469
+#ifdef HAVE_EXTLOG
d9e469
+	rc = add_event_handler(ras, pevent, page_size, "ras", "extlog_mem_event",
d9e469
+			       ras_extlog_mem_event_handler);
d9e469
+	if (!rc) {
d9e469
+		/* tell kernel we are listening, so don't printk to console */
d9e469
+		(void)open("/sys/kernel/debug/ras/daemon_active", 0);
d9e469
+		num_events++;
d9e469
+	} else
d9e469
+		log(ALL, LOG_ERR, "Can't get traces from %s:%s\n",
d9e469
+		    "ras", "aer_event");
d9e469
+#endif
d9e469
+
d9e469
 	if (!num_events) {
d9e469
 		log(ALL, LOG_INFO,
d9e469
 		    "Failed to trace all supported RAS events. Aborting.\n");
d9e469
diff --git a/ras-extlog-handler.c b/ras-extlog-handler.c
d9e469
new file mode 100644
d9e469
index 0000000..5fd3580
d9e469
--- /dev/null
d9e469
+++ b/ras-extlog-handler.c
d9e469
@@ -0,0 +1,246 @@
d9e469
+/*
d9e469
+ * Copyright (C) 2014 Tony Luck <tony.luck@intel.com>
d9e469
+ *
d9e469
+ * This program is free software; you can redistribute it and/or modify
d9e469
+ * it under the terms of the GNU General Public License as published by
d9e469
+ * the Free Software Foundation; either version 2 of the License, or
d9e469
+ * (at your option) any later version.
d9e469
+ *
d9e469
+ * This program is distributed in the hope that it will be useful,
d9e469
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
d9e469
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
d9e469
+ * GNU General Public License for more details.
d9e469
+ *
d9e469
+ * You should have received a copy of the GNU General Public License
d9e469
+ * along with this program; if not, write to the Free Software
d9e469
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
d9e469
+*/
d9e469
+#include <ctype.h>
d9e469
+#include <errno.h>
d9e469
+#include <stdio.h>
d9e469
+#include <stdlib.h>
d9e469
+#include <string.h>
d9e469
+#include <unistd.h>
d9e469
+#include <stdint.h>
d9e469
+#include "libtrace/kbuffer.h"
d9e469
+#include "ras-extlog-handler.h"
d9e469
+#include "ras-record.h"
d9e469
+#include "ras-logger.h"
d9e469
+#include "ras-report.h"
d9e469
+
d9e469
+static char *err_type(int etype)
d9e469
+{
d9e469
+	switch (etype) {
d9e469
+	case 0: return "unknown";
d9e469
+	case 1: return "no error";
d9e469
+	case 2: return "single-bit ECC";
d9e469
+	case 3: return "multi-bit ECC";
d9e469
+	case 4: return "single-symbol chipkill ECC";
d9e469
+	case 5: return "multi-symbol chipkill ECC";
d9e469
+	case 6: return "master abort";
d9e469
+	case 7: return "target abort";
d9e469
+	case 8: return "parity error";
d9e469
+	case 9: return "watchdog timeout";
d9e469
+	case 10: return "invalid address";
d9e469
+	case 11: return "mirror Broken";
d9e469
+	case 12: return "memory sparing";
d9e469
+	case 13: return "scrub corrected error";
d9e469
+	case 14: return "scrub uncorrected error";
d9e469
+	case 15: return "physical memory map-out event";
d9e469
+	}
d9e469
+	return "unknown-type";
d9e469
+}
d9e469
+
d9e469
+static char *err_severity(int severity)
d9e469
+{
d9e469
+	switch (severity) {
d9e469
+	case 0: return "recoverable";
d9e469
+	case 1: return "fatal";
d9e469
+	case 2: return "corrected";
d9e469
+	case 3: return "informational";
d9e469
+	}
d9e469
+	return "unknown-severity";
d9e469
+}
d9e469
+
d9e469
+static unsigned long long err_mask(int lsb)
d9e469
+{
d9e469
+	if (lsb == 0xff)
d9e469
+		return ~0ull;
d9e469
+	return ~((1ull << lsb) - 1);
d9e469
+}
d9e469
+
d9e469
+#define CPER_MEM_VALID_NODE			0x0008
d9e469
+#define CPER_MEM_VALID_CARD			0x0010
d9e469
+#define CPER_MEM_VALID_MODULE			0x0020
d9e469
+#define CPER_MEM_VALID_BANK			0x0040
d9e469
+#define CPER_MEM_VALID_DEVICE			0x0080
d9e469
+#define CPER_MEM_VALID_ROW			0x0100
d9e469
+#define CPER_MEM_VALID_COLUMN			0x0200
d9e469
+#define CPER_MEM_VALID_BIT_POSITION		0x0400
d9e469
+#define CPER_MEM_VALID_REQUESTOR_ID		0x0800
d9e469
+#define CPER_MEM_VALID_RESPONDER_ID		0x1000
d9e469
+#define CPER_MEM_VALID_TARGET_ID		0x2000
d9e469
+#define CPER_MEM_VALID_RANK_NUMBER		0x8000
d9e469
+#define CPER_MEM_VALID_CARD_HANDLE		0x10000
d9e469
+#define CPER_MEM_VALID_MODULE_HANDLE		0x20000
d9e469
+
d9e469
+struct cper_mem_err_compact {
d9e469
+	unsigned long long	validation_bits;
d9e469
+	unsigned short		node;
d9e469
+	unsigned short		card;
d9e469
+	unsigned short		module;
d9e469
+	unsigned short		bank;
d9e469
+	unsigned short		device;
d9e469
+	unsigned short		row;
d9e469
+	unsigned short		column;
d9e469
+	unsigned short		bit_pos;
d9e469
+	unsigned long long	requestor_id;
d9e469
+	unsigned long long	responder_id;
d9e469
+	unsigned long long	target_id;
d9e469
+	unsigned short		rank;
d9e469
+	unsigned short		mem_array_handle;
d9e469
+	unsigned short		mem_dev_handle;
d9e469
+};
d9e469
+
d9e469
+static char *err_cper_data(const char *c)
d9e469
+{
d9e469
+	const struct cper_mem_err_compact *cpd = (struct cper_mem_err_compact *)c;
d9e469
+	static char buf[256];
d9e469
+	char *p = buf;
d9e469
+
d9e469
+	if (cpd->validation_bits == 0)
d9e469
+		return "";
d9e469
+	p += sprintf(p, " (");
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_NODE)
d9e469
+		p += sprintf(p, "node: %d ", cpd->node);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_CARD)
d9e469
+		p += sprintf(p, "card: %d ", cpd->card);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_MODULE)
d9e469
+		p += sprintf(p, "module: %d ", cpd->module);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_BANK)
d9e469
+		p += sprintf(p, "bank: %d ", cpd->bank);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_DEVICE)
d9e469
+		p += sprintf(p, "device: %d ", cpd->device);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_ROW)
d9e469
+		p += sprintf(p, "row: %d ", cpd->row);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_COLUMN)
d9e469
+		p += sprintf(p, "column: %d ", cpd->column);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_BIT_POSITION)
d9e469
+		p += sprintf(p, "bit_pos: %d ", cpd->bit_pos);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_REQUESTOR_ID)
d9e469
+		p += sprintf(p, "req_id: 0x%llx ", cpd->requestor_id);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_RESPONDER_ID)
d9e469
+		p += sprintf(p, "resp_id: 0x%llx ", cpd->responder_id);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_TARGET_ID)
d9e469
+		p += sprintf(p, "tgt_id: 0x%llx ", cpd->target_id);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_RANK_NUMBER)
d9e469
+		p += sprintf(p, "rank: %d ", cpd->rank);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_CARD_HANDLE)
d9e469
+		p += sprintf(p, "card_handle: %d ", cpd->mem_array_handle);
d9e469
+	if (cpd->validation_bits & CPER_MEM_VALID_MODULE_HANDLE)
d9e469
+		p += sprintf(p, "module_handle: %d ", cpd->mem_dev_handle);
d9e469
+	p += sprintf(p-1, ")");
d9e469
+
d9e469
+	return buf;
d9e469
+}
d9e469
+
d9e469
+static char *uuid_le(const char *uu)
d9e469
+{
d9e469
+	static char uuid[sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")];
d9e469
+	char *p = uuid;
d9e469
+	int i;
d9e469
+	static const unsigned char le[16] = {3,2,1,0,5,4,7,6,8,9,10,11,12,13,14,15};
d9e469
+
d9e469
+	for (i = 0; i < 16; i++) {
d9e469
+		p += sprintf(p, "%.2x", uu[le[i]]);
d9e469
+		switch (i) {
d9e469
+		case 3:
d9e469
+		case 5:
d9e469
+		case 7:
d9e469
+		case 9:
d9e469
+			*p++ = '-';
d9e469
+			break;
d9e469
+		}
d9e469
+	}
d9e469
+
d9e469
+	*p = 0;
d9e469
+
d9e469
+	return uuid;
d9e469
+}
d9e469
+
d9e469
+
d9e469
+static void report_extlog_mem_event(struct ras_events *ras,
d9e469
+				    struct pevent_record *record,
d9e469
+				    struct trace_seq *s,
d9e469
+				    struct ras_extlog_event *ev)
d9e469
+{
d9e469
+	trace_seq_printf(s, "%d %s error: %s physical addr: 0x%llx mask: 0x%llx%s %s %s",
d9e469
+		ev->error_seq, err_severity(ev->severity),
d9e469
+		err_type(ev->etype), ev->address,
d9e469
+		err_mask(ev->pa_mask_lsb),
d9e469
+		err_cper_data(ev->cper_data),
d9e469
+		ev->fru_text,
d9e469
+		uuid_le(ev->fru_id));
d9e469
+}
d9e469
+
d9e469
+int ras_extlog_mem_event_handler(struct trace_seq *s,
d9e469
+			  struct pevent_record *record,
d9e469
+			  struct event_format *event, void *context)
d9e469
+{
d9e469
+	int len;
d9e469
+	unsigned long long val;
d9e469
+	struct ras_events *ras = context;
d9e469
+	time_t now;
d9e469
+	struct tm *tm;
d9e469
+	struct ras_extlog_event ev;
d9e469
+
d9e469
+	/*
d9e469
+	 * Newer kernels (3.10-rc1 or upper) provide an uptime clock.
d9e469
+	 * On previous kernels, the way to properly generate an event would
d9e469
+	 * be to inject a fake one, measure its timestamp and diff it against
d9e469
+	 * gettimeofday. We won't do it here. Instead, let's use uptime,
d9e469
+	 * falling-back to the event report's time, if "uptime" clock is
d9e469
+	 * not available (legacy kernels).
d9e469
+	 */
d9e469
+
d9e469
+	if (ras->use_uptime)
d9e469
+		now = record->ts/user_hz + ras->uptime_diff;
d9e469
+	else
d9e469
+		now = time(NULL);
d9e469
+
d9e469
+	tm = localtime(&now;;
d9e469
+	if (tm)
d9e469
+		strftime(ev.timestamp, sizeof(ev.timestamp),
d9e469
+			 "%Y-%m-%d %H:%M:%S %z", tm);
d9e469
+	trace_seq_printf(s, "%s ", ev.timestamp);
d9e469
+
d9e469
+	if (pevent_get_field_val(s,  event, "etype", record, &val, 1) < 0)
d9e469
+		return -1;
d9e469
+	ev.etype = val;
d9e469
+	if (pevent_get_field_val(s,  event, "err_seq", record, &val, 1) < 0)
d9e469
+		return -1;
d9e469
+	ev.error_seq = val;
d9e469
+	if (pevent_get_field_val(s,  event, "sev", record, &val, 1) < 0)
d9e469
+		return -1;
d9e469
+	ev.severity = val;
d9e469
+	if (pevent_get_field_val(s,  event, "pa", record, &val, 1) < 0)
d9e469
+		return -1;
d9e469
+	ev.address = val;
d9e469
+	if (pevent_get_field_val(s,  event, "pa_mask_lsb", record, &val, 1) < 0)
d9e469
+		return -1;
d9e469
+	ev.pa_mask_lsb = val;
d9e469
+
d9e469
+	ev.cper_data = pevent_get_field_raw(s, event, "data",
d9e469
+					   record, &len, 1);
d9e469
+	ev.cper_data_length = len;
d9e469
+	ev.fru_text = pevent_get_field_raw(s, event, "fru_text",
d9e469
+					   record, &len, 1);
d9e469
+	ev.fru_id = pevent_get_field_raw(s, event, "fru_id",
d9e469
+					   record, &len, 1);
d9e469
+
d9e469
+	report_extlog_mem_event(ras, record, s, &ev;;
d9e469
+
d9e469
+	ras_store_extlog_mem_record(ras, &ev;;
d9e469
+
d9e469
+	return 0;
d9e469
+}
d9e469
diff --git a/ras-extlog-handler.h b/ras-extlog-handler.h
d9e469
new file mode 100644
d9e469
index 0000000..54e8cec
d9e469
--- /dev/null
d9e469
+++ b/ras-extlog-handler.h
d9e469
@@ -0,0 +1,31 @@
d9e469
+/*
d9e469
+ * Copyright (C) 2014 Tony Luck <tony.luck@intel.com>
d9e469
+ *
d9e469
+ * This program is free software; you can redistribute it and/or modify
d9e469
+ * it under the terms of the GNU General Public License as published by
d9e469
+ * the Free Software Foundation; either version 2 of the License, or
d9e469
+ * (at your option) any later version.
d9e469
+ *
d9e469
+ * This program is distributed in the hope that it will be useful,
d9e469
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
d9e469
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
d9e469
+ * GNU General Public License for more details.
d9e469
+ *
d9e469
+ * You should have received a copy of the GNU General Public License
d9e469
+ * along with this program; if not, write to the Free Software
d9e469
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
d9e469
+*/
d9e469
+
d9e469
+#ifndef __RAS_EXTLOG_HANDLER_H
d9e469
+#define __RAS_EXTLOG_HANDLER_H
d9e469
+
d9e469
+#include <stdint.h>
d9e469
+
d9e469
+#include "ras-events.h"
d9e469
+#include "libtrace/event-parse.h"
d9e469
+
d9e469
+extern int ras_extlog_mem_event_handler(struct trace_seq *s,
d9e469
+			  struct pevent_record *record,
d9e469
+			  struct event_format *event, void *context);
d9e469
+
d9e469
+#endif
d9e469
diff --git a/ras-mc-handler.c b/ras-mc-handler.c
d9e469
index ffb3805..704a41c 100644
d9e469
--- a/ras-mc-handler.c
d9e469
+++ b/ras-mc-handler.c
d9e469
@@ -47,7 +47,7 @@ int ras_mc_event_handler(struct trace_seq *s,
d9e469
 	 */
d9e469
 
d9e469
 	if (ras->use_uptime)
d9e469
-		now = record->ts/1000000000L + ras->uptime_diff;
d9e469
+		now = record->ts/user_hz + ras->uptime_diff;
d9e469
 	else
d9e469
 		now = time(NULL);
d9e469
 
d9e469
diff --git a/ras-mce-handler.c b/ras-mce-handler.c
d9e469
index 1431049..a1d0b5d 100644
d9e469
--- a/ras-mce-handler.c
d9e469
+++ b/ras-mce-handler.c
d9e469
@@ -237,7 +237,7 @@ static void report_mce_event(struct ras_events *ras,
d9e469
 	 */
d9e469
 
d9e469
 	if (ras->use_uptime)
d9e469
-		now = record->ts/1000000000L + ras->uptime_diff;
d9e469
+		now = record->ts/user_hz + ras->uptime_diff;
d9e469
 	else
d9e469
 		now = time(NULL);
d9e469
 
d9e469
diff --git a/ras-record.c b/ras-record.c
d9e469
index e5150ad..3dc4493 100644
d9e469
--- a/ras-record.c
d9e469
+++ b/ras-record.c
d9e469
@@ -157,6 +157,57 @@ int ras_store_aer_event(struct ras_events *ras, struct ras_aer_event *ev)
d9e469
 }
d9e469
 #endif
d9e469
 
d9e469
+#ifdef HAVE_EXTLOG
d9e469
+static const struct db_fields extlog_event_fields[] = {
d9e469
+		{ .name="id",			.type="INTEGER PRIMARY KEY" },
d9e469
+		{ .name="timestamp",		.type="TEXT" },
d9e469
+		{ .name="etype",		.type="INTEGER" },
d9e469
+		{ .name="error_count",		.type="INTEGER" },
d9e469
+		{ .name="severity",		.type="INTEGER" },
d9e469
+		{ .name="address",		.type="INTEGER" },
d9e469
+		{ .name="fru_id",		.type="BLOB" },
d9e469
+		{ .name="fru_text",		.type="TEXT" },
d9e469
+		{ .name="cper_data",		.type="BLOB" },
d9e469
+};
d9e469
+
d9e469
+static const struct db_table_descriptor extlog_event_tab = {
d9e469
+	.name = "extlog_event",
d9e469
+	.fields = extlog_event_fields,
d9e469
+	.num_fields = ARRAY_SIZE(extlog_event_fields),
d9e469
+};
d9e469
+
d9e469
+int ras_store_extlog_mem_record(struct ras_events *ras, struct ras_extlog_event *ev)
d9e469
+{
d9e469
+	int rc;
d9e469
+	struct sqlite3_priv *priv = ras->db_priv;
d9e469
+
d9e469
+	if (!priv || !priv->stmt_extlog_record)
d9e469
+		return 0;
d9e469
+	log(TERM, LOG_INFO, "extlog_record store: %p\n", priv->stmt_extlog_record);
d9e469
+
d9e469
+	sqlite3_bind_text  (priv->stmt_extlog_record,  1, ev->timestamp, -1, NULL);
d9e469
+	sqlite3_bind_int   (priv->stmt_extlog_record,  2, ev->etype);
d9e469
+	sqlite3_bind_int   (priv->stmt_extlog_record,  3, ev->error_seq);
d9e469
+	sqlite3_bind_int   (priv->stmt_extlog_record,  4, ev->severity);
d9e469
+	sqlite3_bind_int64 (priv->stmt_extlog_record,  5, ev->address);
d9e469
+	sqlite3_bind_blob  (priv->stmt_extlog_record,  6, ev->fru_id, 16, NULL);
d9e469
+	sqlite3_bind_text  (priv->stmt_extlog_record,  7, ev->fru_text, -1, NULL);
d9e469
+	sqlite3_bind_blob  (priv->stmt_extlog_record,  8, ev->cper_data, ev->cper_data_length, NULL);
d9e469
+
d9e469
+	rc = sqlite3_step(priv->stmt_extlog_record);
d9e469
+	if (rc != SQLITE_OK && rc != SQLITE_DONE)
d9e469
+		log(TERM, LOG_ERR,
d9e469
+		    "Failed to do extlog_mem_record step on sqlite: error = %d\n", rc);
d9e469
+	rc = sqlite3_reset(priv->stmt_extlog_record);
d9e469
+	if (rc != SQLITE_OK && rc != SQLITE_DONE)
d9e469
+		log(TERM, LOG_ERR,
d9e469
+		    "Failed reset extlog_mem_record on sqlite: error = %d\n",
d9e469
+		    rc);
d9e469
+	log(TERM, LOG_INFO, "register inserted at db\n");
d9e469
+
d9e469
+	return rc;
d9e469
+}
d9e469
+#endif
d9e469
 
d9e469
 /*
d9e469
  * Table and functions to handle mce:mce_record
d9e469
@@ -385,6 +436,13 @@ int ras_mc_event_opendb(unsigned cpu, struct ras_events *ras)
d9e469
 					 &aer_event_tab);
d9e469
 #endif
d9e469
 
d9e469
+#ifdef HAVE_EXTLOG
d9e469
+	rc = ras_mc_create_table(priv, &extlog_event_tab);
d9e469
+	if (rc == SQLITE_OK)
d9e469
+		rc = ras_mc_prepare_stmt(priv, &priv->stmt_extlog_record,
d9e469
+					 &extlog_event_tab);
d9e469
+#endif
d9e469
+
d9e469
 #ifdef HAVE_MCE
d9e469
 	rc = ras_mc_create_table(priv, &mce_record_tab);
d9e469
 	if (rc == SQLITE_OK)
d9e469
diff --git a/ras-record.h b/ras-record.h
d9e469
index 6f146a8..5d84297 100644
d9e469
--- a/ras-record.h
d9e469
+++ b/ras-record.h
d9e469
@@ -19,8 +19,11 @@
d9e469
 #ifndef __RAS_RECORD_H
d9e469
 #define __RAS_RECORD_H
d9e469
 
d9e469
+#include <stdint.h>
d9e469
 #include "config.h"
d9e469
 
d9e469
+extern long user_hz;
d9e469
+
d9e469
 struct ras_events *ras;
d9e469
 
d9e469
 struct ras_mc_event {
d9e469
@@ -40,8 +43,22 @@ struct ras_aer_event {
d9e469
 	const char *msg;
d9e469
 };
d9e469
 
d9e469
+struct ras_extlog_event {
d9e469
+	char timestamp[64];
d9e469
+	int32_t error_seq;
d9e469
+	int8_t etype;
d9e469
+	int8_t severity;
d9e469
+	unsigned long long address;
d9e469
+	int8_t pa_mask_lsb;
d9e469
+	const char *fru_id;
d9e469
+	const char *fru_text;
d9e469
+	const char *cper_data;
d9e469
+	unsigned short cper_data_length;
d9e469
+};
d9e469
+
d9e469
 struct ras_mc_event;
d9e469
 struct ras_aer_event;
d9e469
+struct ras_extlog_event;
d9e469
 struct mce_event;
d9e469
 
d9e469
 #ifdef HAVE_SQLITE3
d9e469
@@ -57,18 +74,23 @@ struct sqlite3_priv {
d9e469
 #ifdef HAVE_MCE
d9e469
 	sqlite3_stmt	*stmt_mce_record;
d9e469
 #endif
d9e469
+#ifdef HAVE_EXTLOG
d9e469
+	sqlite3_stmt	*stmt_extlog_record;
d9e469
+#endif
d9e469
 };
d9e469
 
d9e469
 int ras_mc_event_opendb(unsigned cpu, struct ras_events *ras);
d9e469
 int ras_store_mc_event(struct ras_events *ras, struct ras_mc_event *ev);
d9e469
 int ras_store_aer_event(struct ras_events *ras, struct ras_aer_event *ev);
d9e469
 int ras_store_mce_record(struct ras_events *ras, struct mce_event *ev);
d9e469
+int ras_store_extlog_mem_record(struct ras_events *ras, struct ras_extlog_event *ev);
d9e469
 
d9e469
 #else
d9e469
 static inline int ras_mc_event_opendb(unsigned cpu, struct ras_events *ras) { return 0; };
d9e469
 static inline int ras_store_mc_event(struct ras_events *ras, struct ras_mc_event *ev) { return 0; };
d9e469
 static inline int ras_store_aer_event(struct ras_events *ras, struct ras_aer_event *ev) { return 0; };
d9e469
 static inline int ras_store_mce_record(struct ras_events *ras, struct mce_event *ev) { return 0; };
d9e469
+static inline int ras_store_extlog_mem_record(struct ras_events *ras, struct ras_extlog_event *ev) { return 0; };
d9e469
 
d9e469
 #endif
d9e469
 
d9e469
diff --git a/rasdaemon.c b/rasdaemon.c
d9e469
index 85ac2d4..41022ef 100644
d9e469
--- a/rasdaemon.c
d9e469
+++ b/rasdaemon.c
d9e469
@@ -68,6 +68,8 @@ static error_t parse_opt(int k, char *arg, struct argp_state *state)
d9e469
 	return 0;
d9e469
 }
d9e469
 
d9e469
+long user_hz;
d9e469
+
d9e469
 int main(int argc, char *argv[])
d9e469
 {
d9e469
 	struct arguments args;
d9e469
@@ -91,6 +93,8 @@ int main(int argc, char *argv[])
d9e469
 	};
d9e469
 	memset (&args, 0, sizeof(args));
d9e469
 
d9e469
+	user_hz = sysconf(_SC_CLK_TCK);
d9e469
+
d9e469
 	argp_parse(&argp, argc, argv, 0,  &idx, &args);
d9e469
 
d9e469
 	if (idx < 0) {
d9e469
diff --git a/util/ras-mc-ctl.in b/util/ras-mc-ctl.in
d9e469
index e9f9c59..110262f 100755
d9e469
--- a/util/ras-mc-ctl.in
d9e469
+++ b/util/ras-mc-ctl.in
d9e469
@@ -842,11 +842,141 @@ sub find_prog
d9e469
     return "";
d9e469
 }
d9e469
 
d9e469
+sub get_extlog_type
d9e469
+{
d9e469
+    my @types;
d9e469
+
d9e469
+    if ($_[0] < 0 || $_[0] > 15) {
d9e469
+        return "unknown-type";
d9e469
+    }
d9e469
+
d9e469
+    @types = ("unknown",
d9e469
+              "no error",
d9e469
+              "single-bit ECC",
d9e469
+              "multi-bit ECC",
d9e469
+              "single-symbol chipkill ECC",
d9e469
+              "multi-symbol chipkill ECC",
d9e469
+              "master abort",
d9e469
+              "target abort",
d9e469
+              "parity error",
d9e469
+              "watchdog timeout",
d9e469
+              "invalid address",
d9e469
+              "mirror Broken",
d9e469
+              "memory sparing",
d9e469
+              "scrub corrected error",
d9e469
+              "scrub uncorrected error",
d9e469
+              "physical memory map-out event",
d9e469
+              "unknown-type");
d9e469
+    return $types[$_[0]];
d9e469
+}
d9e469
+
d9e469
+sub get_extlog_severity
d9e469
+{
d9e469
+    my @sev;
d9e469
+
d9e469
+    if ($_[0] < 0 || $_[0] > 3) {
d9e469
+        return "unknown-severity";
d9e469
+    }
d9e469
+
d9e469
+    @sev = ("recoverable",
d9e469
+            "fatal",
d9e469
+            "corrected",
d9e469
+            "informational",
d9e469
+            "unknown-severity");
d9e469
+    return $sev[$_[0]];
d9e469
+}
d9e469
+
d9e469
+use constant {
d9e469
+    CPER_MEM_VALID_NODE => 0x0008,
d9e469
+    CPER_MEM_VALID_CARD => 0x0010,
d9e469
+    CPER_MEM_VALID_MODULE => 0x0020,
d9e469
+    CPER_MEM_VALID_BANK => 0x0040,
d9e469
+    CPER_MEM_VALID_DEVICE => 0x0080,
d9e469
+    CPER_MEM_VALID_ROW => 0x0100,
d9e469
+    CPER_MEM_VALID_COLUMN => 0x0200,
d9e469
+    CPER_MEM_VALID_BIT_POSITION => 0x0400,
d9e469
+    CPER_MEM_VALID_REQUESTOR_ID => 0x0800,
d9e469
+    CPER_MEM_VALID_RESPONDER_ID => 0x1000,
d9e469
+    CPER_MEM_VALID_TARGET_ID => 0x2000,
d9e469
+    CPER_MEM_VALID_ERROR_TYPE => 0x4000,
d9e469
+    CPER_MEM_VALID_RANK_NUMBER => 0x8000,
d9e469
+    CPER_MEM_VALID_CARD_HANDLE => 0x10000,
d9e469
+    CPER_MEM_VALID_MODULE_HANDLE => 0x20000,
d9e469
+};
d9e469
+
d9e469
+sub get_cper_data_text
d9e469
+{
d9e469
+    my $cper_data = $_[0];
d9e469
+    my ($validation_bits, $node, $card, $module, $bank, $device, $row, $column, $bit_pos, $requestor_id, $responder_id, $target_id, $rank, $mem_array_handle, $mem_dev_handle) = unpack 'QSSSSSSSSQQQSSS', $cper_data;
d9e469
+    my @out;
d9e469
+
d9e469
+    if ($validation_bits & CPER_MEM_VALID_NODE) {
d9e469
+        push @out, (sprintf "node=%d", $node);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_CARD) {
d9e469
+        push @out, (sprintf "card=%d", $card);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_MODULE) {
d9e469
+        push @out, (sprintf "module=%d", $module);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_BANK) {
d9e469
+        push @out, (sprintf "bank=%d", $bank);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_DEVICE) {
d9e469
+        push @out, (sprintf "device=%d", $device);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_ROW) {
d9e469
+        push @out, (sprintf "row=%d", $row);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_COLUMN) {
d9e469
+        push @out, (sprintf "column=%d", $column);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_BIT_POSITION) {
d9e469
+        push @out, (sprintf "bit_position=%d", $bit_pos);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_REQUESTOR_ID) {
d9e469
+        push @out, (sprintf "0x%08x", $requestor_id);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_RESPONDER_ID) {
d9e469
+        push @out, (sprintf "0x%08x", $responder_id);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_TARGET_ID) {
d9e469
+        push @out, (sprintf "0x%08x", $target_id);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_RANK_NUMBER) {
d9e469
+        push @out, (sprintf "rank=%d", $rank);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_CARD_HANDLE) {
d9e469
+        push @out, (sprintf "mem_array_handle=%d", $mem_array_handle);
d9e469
+    }
d9e469
+    if ($validation_bits & CPER_MEM_VALID_MODULE_HANDLE) {
d9e469
+        push @out, (sprintf "mem_dev_handle=%d", $mem_dev_handle);
d9e469
+    }
d9e469
+
d9e469
+    return join (", ", @out);
d9e469
+}
d9e469
+
d9e469
+sub get_uuid_le
d9e469
+{
d9e469
+    my $out = "";
d9e469
+    my @bytes = unpack "C*", $_[0];
d9e469
+    my @le16_table = (3, 2, 1, 0, 5, 4, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15);
d9e469
+
d9e469
+    for (my $i = 0; $i < 16; $i++) {
d9e469
+        $out .= sprintf "%.2x", $bytes[$le16_table[$i]];
d9e469
+        if ($i == 3 or $i == 5 or $i == 7 or $i == 9) {
d9e469
+            $out .= "-";
d9e469
+        }
d9e469
+    }
d9e469
+    return $out;
d9e469
+}
d9e469
+
d9e469
 sub summary
d9e469
 {
d9e469
     require DBI;
d9e469
     my ($query, $query_handle, $out);
d9e469
     my ($err_type, $label, $mc, $top, $mid, $low, $count, $msg);
d9e469
+    my ($etype, $severity, $etype_string, $severity_string);
d9e469
 
d9e469
     my $dbh = DBI->connect("dbi:SQLite:dbname=$dbname", "", "", {});
d9e469
 
d9e469
@@ -882,6 +1012,24 @@ sub summary
d9e469
     }
d9e469
     $query_handle->finish;
d9e469
 
d9e469
+    # extlog errors
d9e469
+    $query = "select etype, severity, count(*) from extlog_event group by etype, severity";
d9e469
+    $query_handle = $dbh->prepare($query);
d9e469
+    $query_handle->execute();
d9e469
+    $query_handle->bind_columns(\($etype, $severity, $count));
d9e469
+    $out = "";
d9e469
+    while($query_handle->fetch()) {
d9e469
+        $etype_string = get_extlog_type($etype);
d9e469
+        $severity_string = get_extlog_severity($severity);
d9e469
+        $out .= "\t$count $etype_string $severity_string errors\n";
d9e469
+    }
d9e469
+    if ($out ne "") {
d9e469
+        print "Extlog records summary:\n$out";
d9e469
+    } else {
d9e469
+        print "No Extlog errors.\n";
d9e469
+    }
d9e469
+    $query_handle->finish;
d9e469
+
d9e469
     # MCE mce_record errors
d9e469
     $query = "select error_msg, count(*) from mce_record group by error_msg";
d9e469
     $query_handle = $dbh->prepare($query);
d9e469
@@ -906,6 +1054,7 @@ sub errors
d9e469
     require DBI;
d9e469
     my ($query, $query_handle, $id, $time, $count, $type, $msg, $label, $mc, $top, $mid, $low, $addr, $grain, $syndrome, $detail, $out);
d9e469
     my ($mcgcap,$mcgstatus, $status, $misc, $ip, $tsc, $walltime, $cpu, $cpuid, $apicid, $socketid, $cs, $bank, $cpuvendor, $bank_name, $mcgstatus_msg, $mcistatus_msg, $user_action, $mc_location);
d9e469
+    my ($timestamp, $etype, $severity, $etype_string, $severity_string, $fru_id, $fru_text, $cper_data);
d9e469
 
d9e469
     my $dbh = DBI->connect("dbi:SQLite:dbname=$dbname", "", "", {});
d9e469
 
d9e469
@@ -945,6 +1094,31 @@ sub errors
d9e469
     }
d9e469
     $query_handle->finish;
d9e469
 
d9e469
+    # Extlog errors
d9e469
+    $query = "select id, timestamp, etype, severity, address, fru_id, fru_text, cper_data from extlog_event order by id";
d9e469
+    $query_handle = $dbh->prepare($query);
d9e469
+    $query_handle->execute();
d9e469
+    $query_handle->bind_columns(\($id, $timestamp, $etype, $severity, $addr, $fru_id, $fru_text, $cper_data));
d9e469
+    $out = "";
d9e469
+    while($query_handle->fetch()) {
d9e469
+        $etype_string = get_extlog_type($etype);
d9e469
+        $severity_string = get_extlog_severity($severity);
d9e469
+        $out .= "$id $timestamp error: ";
d9e469
+        $out .= "type=$etype_string, ";
d9e469
+        $out .= "severity=$severity_string, ";
d9e469
+        $out .= sprintf "address=0x%08x, ", $addr;
d9e469
+        $out .= sprintf "fru_id=%s, ", get_uuid_le($fru_id);
d9e469
+        $out .= "fru_text='$fru_text', ";
d9e469
+        $out .= get_cper_data_text($cper_data) if ($cper_data);
d9e469
+        $out .= "\n";
d9e469
+    }
d9e469
+    if ($out ne "") {
d9e469
+        print "Extlog events:\n$out\n";
d9e469
+    } else {
d9e469
+        print "No Extlog errors.\n\n";
d9e469
+    }
d9e469
+    $query_handle->finish;
d9e469
+
d9e469
     # MCE mce_record errors
d9e469
     $query = "select id, timestamp, mcgcap, mcgstatus, status, addr, misc, ip, tsc, walltime, cpu, cpuid, apicid, socketid, cs, bank, cpuvendor, bank_name, error_msg, mcgstatus_msg, mcistatus_msg, user_action, mc_location from mce_record order by id";
d9e469
     $query_handle = $dbh->prepare($query);