<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <p><br>
    </p>
    <br>
    <div class="moz-cite-prefix">On 07/17/2018 04:41 PM, Marcello
      Sylvester Bauer wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:20180717084126.19924-1-info@marcellobauer.com">
      <pre wrap="">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 <a class="moz-txt-link-freetext" href="https://review.coreboot.org/coreboot.git">https://review.coreboot.org/coreboot.git</a>

Signed-off-by: Marcello Sylvester Bauer <a class="moz-txt-link-rfc2396E" href="mailto:info@marcellobauer.com"><info@marcellobauer.com></a>
---
 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
</pre>
    </blockquote>
    <br>
    Acked-by: Ivan Hu <a class="moz-txt-link-rfc2396E"
      href="mailto:ivan.hu@canonical.com"><ivan.hu@canonical.com></a>
  </body>
</html>