[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