ACK: [PATCH] fwts_coreboot.c: add cbmem console parser
ivanhu
ivan.hu at canonical.com
Mon Jul 23 09:58:07 UTC 2018
On 07/17/2018 04:41 PM, Marcello Sylvester Bauer wrote:
> 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
Acked-by: Ivan Hu <ivan.hu at canonical.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.ubuntu.com/archives/fwts-devel/attachments/20180723/5bd4bfa3/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 473 bytes
Desc: OpenPGP digital signature
URL: <https://lists.ubuntu.com/archives/fwts-devel/attachments/20180723/5bd4bfa3/attachment-0001.sig>
More information about the fwts-devel
mailing list