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