[PATCH] fwts_coreboot.c: add cbmem console parser

Marcello Sylvester Bauer info at marcellobauer.com
Tue Jul 17 08:41:26 UTC 2018


Parse the full coreboot console log out of /dev/mem, if the google
kernel module is not loaded. Does only work on x86 plattform.

Todo:
- add arm plattform support

Tested with:
- Lenovo thinkpad t410
- Qemu x86

The code is based on:
- google kernel module memconsole-coreboot
- coreboot util/cbmem https://review.coreboot.org/coreboot.git

Signed-off-by: Marcello Sylvester Bauer <info at marcellobauer.com>
---
 src/lib/include/fwts.h            |   1 +
 src/lib/include/fwts_coreboot.h   |  35 ++++
 src/lib/src/Makefile.am           |   2 +
 src/lib/src/fwts_clog.c           |   3 +-
 src/lib/src/fwts_coreboot.c       |  52 ++++++
 src/lib/src/fwts_coreboot_cbmem.c | 341 ++++++++++++++++++++++++++++++++++++++
 6 files changed, 433 insertions(+), 1 deletion(-)
 create mode 100644 src/lib/include/fwts_coreboot.h
 create mode 100644 src/lib/src/fwts_coreboot.c
 create mode 100644 src/lib/src/fwts_coreboot_cbmem.c

diff --git a/src/lib/include/fwts.h b/src/lib/include/fwts.h
index 95e8f560..5cc0c3ae 100644
--- a/src/lib/include/fwts.h
+++ b/src/lib/include/fwts.h
@@ -89,6 +89,7 @@
 #include "fwts_arch.h"
 #include "fwts_checkeuid.h"
 #include "fwts_clog.h"
+#include "fwts_coreboot.h"
 #include "fwts_cpu.h"
 #include "fwts_dump.h"
 #include "fwts_dump_data.h"
diff --git a/src/lib/include/fwts_coreboot.h b/src/lib/include/fwts_coreboot.h
new file mode 100644
index 00000000..ae799639
--- /dev/null
+++ b/src/lib/include/fwts_coreboot.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2010-2018 Canonical
+ * Copyright (C) 2017 Google Inc.
+ * Copyright (C) 2018 9elements Cyber Security
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __FWTS_COREBOOT_H__
+#define __FWTS_COREBOOT_H__
+
+#include "fwts.h"
+
+#ifdef FWTS_ARCH_INTEL
+
+extern char *fwts_coreboot_cbmem_console_dump(void);
+
+#endif
+
+fwts_list* fwts_coreboot_cbmem_log(void);
+
+#endif
diff --git a/src/lib/src/Makefile.am b/src/lib/src/Makefile.am
index 77e99eae..d46a21ea 100644
--- a/src/lib/src/Makefile.am
+++ b/src/lib/src/Makefile.am
@@ -61,6 +61,8 @@ libfwts_la_SOURCES = 		\
 	fwts_checksum.c 	\
 	fwts_clog.c		\
 	fwts_cmos.c 		\
+	fwts_coreboot.c		\
+	fwts_coreboot_cbmem.c	\
 	fwts_cpu.c 		\
 	fwts_dump.c 		\
 	fwts_dump_data.c 	\
diff --git a/src/lib/src/fwts_clog.c b/src/lib/src/fwts_clog.c
index 8c64e765..a8b04413 100644
--- a/src/lib/src/fwts_clog.c
+++ b/src/lib/src/fwts_clog.c
@@ -68,7 +68,6 @@ bool fwts_clog_available(fwts_framework *fw)
 
 /*
  *  read coreboot log and return as list of lines
- *  TODO: find coreboot log in /dev/mem
  */
 fwts_list *fwts_clog_read(fwts_framework *fw)
 {
@@ -78,6 +77,8 @@ fwts_list *fwts_clog_read(fwts_framework *fw)
         return list;
     if ((list = fwts_file_open_and_read(GOOGLE_MEMCONSOLE_COREBOOT_PATH)) != NULL)
         return list;
+    if ((list = fwts_coreboot_cbmem_log()) != NULL)
+       return list;
 
     return NULL;
 }
diff --git a/src/lib/src/fwts_coreboot.c b/src/lib/src/fwts_coreboot.c
new file mode 100644
index 00000000..42d3f6f8
--- /dev/null
+++ b/src/lib/src/fwts_coreboot.c
@@ -0,0 +1,52 @@
+ /*
+ * Copyright (C) 2010-2018 Canonical
+ * Copyright (C) 2017 Google Inc.
+ * Copyright (C) 2018 9elements Cyber Security
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "fwts.h"
+
+#ifdef FWTS_ARCH_INTEL
+
+fwts_list* fwts_coreboot_cbmem_log(void)
+{
+	fwts_list *console_list;
+	char *console;
+
+	console = fwts_coreboot_cbmem_console_dump();
+
+	if (!console)
+		return NULL;
+
+	console_list = fwts_list_from_text(console);
+	free(console);
+
+	return console_list;
+}
+
+#else
+
+fwts_list* fwts_coreboot_cbmem_log(void)
+{
+	/*
+	 * TODO: add arm plattform support
+	 */
+	return NULL;
+}
+
+#endif
diff --git a/src/lib/src/fwts_coreboot_cbmem.c b/src/lib/src/fwts_coreboot_cbmem.c
new file mode 100644
index 00000000..ae9276d9
--- /dev/null
+++ b/src/lib/src/fwts_coreboot_cbmem.c
@@ -0,0 +1,341 @@
+ /*
+ * Copyright (C) 2010-2018 Canonical
+ * Copyright (C) 2017 Google Inc.
+ * Copyright (C) 2018 9elements Cyber Security
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "fwts.h"
+
+#ifdef FWTS_ARCH_INTEL
+
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+
+
+#define CURSOR_MASK ((1 << 28) - 1)
+#define OVERFLOW (1 << 31)
+
+#define LB_TAG_CBMEM_CONSOLE	0x0017
+#define LB_TAG_FORWARD		0x0011
+
+#define MIN(a,b) ((a)<(b) ? (a):(b))
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+struct lb_record {
+        uint32_t tag;           /* tag ID */
+        uint32_t size;          /* size of record (in bytes) */
+} __attribute__ ((packed));
+
+struct lb_header {
+        uint8_t  signature[4]; /* LBIO */
+        uint32_t header_bytes;
+        uint32_t header_checksum;
+        uint32_t table_bytes;
+        uint32_t table_checksum;
+        uint32_t table_entries;
+} __attribute__ ((packed));
+
+struct lb_forward {
+        uint32_t tag;
+        uint32_t size;
+        uint64_t forward;
+} __attribute__ ((packed));
+
+struct lb_cbmem_ref {
+        uint32_t tag;
+        uint32_t size;
+
+        uint64_t cbmem_addr;
+} __attribute__ ((packed));
+
+struct cbmem_console {
+	uint32_t size;
+	uint32_t cursor;
+	uint8_t  body[0];
+} __attribute__ ((packed));
+
+/* Return < 0 on error, 0 on success. */
+static int parse_cbtable(uint64_t address, size_t table_size, uint64_t *cbmen_console_addr);
+
+static void *map_memory(unsigned long long addr, size_t size)
+{
+	void *mem;
+	void *phy;
+
+	phy = fwts_mmap(addr, size);
+
+	if (phy == FWTS_MAP_FAILED)
+		return NULL;
+
+	mem = malloc(size);
+
+	if (!mem) {
+		fwts_munmap(phy, size);
+		return NULL;
+	}
+
+	memcpy(mem, phy, size);
+	fwts_munmap(phy, size);
+
+	return mem;
+}
+
+/*
+ * calculate ip checksum (16 bit quantities) on a passed in buffer. In case
+ * the buffer length is odd last byte is excluded from the calculation
+ */
+static uint16_t ipchcksum(const void *addr, unsigned size)
+{
+	const uint16_t *p = addr;
+	unsigned i, n = size / 2; /* don't expect odd sized blocks */
+	uint32_t sum = 0;
+
+	for (i = 0; i < n; i++)
+		sum += p[i];
+
+	sum = (sum >> 16) + (sum & 0xffff);
+	sum += (sum >> 16);
+	sum = ~sum & 0xffff;
+
+	return (uint16_t) sum;
+}
+
+/* This is a work-around for a nasty problem introduced by initially having
+ * pointer sized entries in the lb_cbmem_ref structures. This caused problems
+ * on 64bit x86 systems because coreboot is 32bit on those systems.
+ * When the problem was found, it was corrected, but there are a lot of
+ * systems out there with a firmware that does not produce the right
+ * lb_cbmem_ref structure. Hence we try to autocorrect this issue here.
+ */
+static struct lb_cbmem_ref parse_cbmem_ref(const struct lb_cbmem_ref *cbmem_ref)
+{
+	struct lb_cbmem_ref ret;
+
+	ret = *cbmem_ref;
+
+	if (cbmem_ref->size < sizeof(*cbmem_ref))
+		ret.cbmem_addr = (uint32_t)ret.cbmem_addr;
+
+	return ret;
+}
+
+/* Return < 0 on error, 0 on success, 1 if forwarding table entry found. */
+static int parse_cbtable_entries(const void *lbtable, size_t table_size, uint64_t *cbmem_console_addr)
+{
+	size_t i;
+	const struct lb_record* lbr_p;
+	int forwarding_table_found = 0;
+
+	for (i = 0; i < table_size; i += lbr_p->size) {
+		lbr_p = lbtable + i;
+		switch (lbr_p->tag) {
+		case LB_TAG_CBMEM_CONSOLE: {
+			*cbmem_console_addr = parse_cbmem_ref((struct lb_cbmem_ref *) lbr_p).cbmem_addr;
+			if (cbmem_console_addr)
+				return 0;
+			continue;
+		}
+		case LB_TAG_FORWARD: {
+			int ret;
+			/*
+			 * This is a forwarding entry - repeat the
+			 * search at the new address.
+			 */
+			struct lb_forward lbf_p =
+				*(const struct lb_forward *) lbr_p;
+			ret = parse_cbtable(lbf_p.forward, 0, cbmem_console_addr);
+
+			/* Assume the forwarding entry is valid. If this fails
+			 * then there's a total failure. */
+			if (ret < 0)
+				return -1;
+			forwarding_table_found = 1;
+		}
+		default:
+			break;
+		}
+	}
+
+	return forwarding_table_found;
+}
+
+/* Return < 0 on error, 0 on success. */
+static int parse_cbtable(uint64_t address, size_t table_size, uint64_t *cbmem_console_table)
+{
+	void *buf;
+	size_t req_size;
+	size_t i;
+
+	req_size = table_size;
+
+	/* Default to 4 KiB search space. */
+	if (req_size == 0)
+		req_size = 4 * 1024;
+
+	buf = map_memory(address, req_size);
+
+	if (!buf)
+		return -1;
+
+	/* look at every 16 bytes */
+	for (i = 0; i <= req_size - sizeof(struct lb_header); i += 16) {
+		int ret;
+		const struct lb_header *lbh;
+		void *map;
+
+		lbh = buf + i;
+
+		if (memcmp(lbh->signature, "LBIO", sizeof(lbh->signature)) ||
+		    !lbh->header_bytes ||
+		    ipchcksum(lbh, sizeof(*lbh))) {
+			continue;
+		}
+
+		/* Map in the whole table to parse. */
+		if (!(map = map_memory(address + i + lbh->header_bytes,
+				 lbh->table_bytes))) {
+			continue;
+		}
+
+		if (ipchcksum(map, lbh->table_bytes) !=
+		    lbh->table_checksum) {
+			free(map);
+			continue;
+		}
+
+		ret = parse_cbtable_entries(map,lbh->table_bytes, cbmem_console_table);
+
+		/* Table parsing failed. */
+		if (ret < 0) {
+			free(map);
+			continue;
+		}
+
+		free(buf);
+		free(map);
+
+		return 0;
+	}
+
+	free(buf);
+
+	return -1;
+}
+
+static ssize_t memory_read_from_buffer(void *to, size_t count, size_t *ppos,
+				const void *from, size_t available)
+{
+	size_t pos = *ppos;
+
+	if (pos >= available)
+		return 0;
+
+	if (count > available - pos)
+		count = available - pos;
+
+	memcpy(to, from+pos, count);
+
+	*ppos = pos + count;
+
+	return count;
+}
+
+static ssize_t memconsole_coreboot_read(struct cbmem_console *con, char *buf, size_t pos, size_t count)
+{
+	uint32_t cursor = con->cursor & CURSOR_MASK;
+	uint32_t flags = con->cursor & ~CURSOR_MASK;
+	struct seg {	/* describes ring buffer segments in logical order */
+		uint32_t phys;	/* physical offset from start of mem buffer */
+		uint32_t len;	/* length of segment */
+	} seg[2] = { {0}, {0} };
+	size_t done = 0;
+	unsigned int i;
+
+	if (flags & OVERFLOW) {
+		if (cursor > count)	/* Shouldn't really happen, but... */
+			cursor = 0;
+		seg[0] = (struct seg){.phys = cursor, .len = count - cursor};
+		seg[1] = (struct seg){.phys = 0, .len = cursor};
+	} else {
+		seg[0] = (struct seg){.phys = 0, .len = MIN(cursor, count)};
+	}
+
+	for (i = 0; i < ARRAY_SIZE(seg) && count > done; i++) {
+		done += memory_read_from_buffer(buf + done, count - done, &pos,
+			con->body + seg[i].phys, seg[i].len);
+		pos -= seg[i].len;
+	}
+
+	return done;
+}
+
+char *fwts_coreboot_cbmem_console_dump(void)
+{
+	unsigned int j;
+	uint64_t cbmem_console_addr;
+	unsigned long long possible_base_addresses[] = { 0, 0xf0000 };
+	struct cbmem_console *console_p;
+	struct cbmem_console *console;
+	char *coreboot_log;
+	ssize_t count;
+
+	/* Find and parse coreboot table */
+	for (j = 0; j < ARRAY_SIZE(possible_base_addresses); j++) {
+		if (!parse_cbtable(possible_base_addresses[j], 0, &cbmem_console_addr))
+			break;
+	}
+	if (j == ARRAY_SIZE(possible_base_addresses))
+		return NULL;
+
+	console_p = map_memory(cbmem_console_addr, sizeof(*console_p));
+	if (console_p == NULL)
+		return NULL;
+
+	console = map_memory(cbmem_console_addr, console_p->size + sizeof(*console));
+	if (console == NULL) {
+		free(console_p);
+		return NULL;
+	}
+
+	free(console_p);
+
+	coreboot_log = malloc(console->size+1);
+	if (!coreboot_log) {
+		free(console);
+		return NULL;
+	}
+
+	coreboot_log[console->size+1] = '\0';
+
+	count = memconsole_coreboot_read(console, coreboot_log, 0, console->size);
+	free(console);
+
+	if (count == 0) {
+		free(coreboot_log);
+		return NULL;
+	}
+
+	return coreboot_log;
+}
+
+#endif
-- 
2.16.4




More information about the fwts-devel mailing list