Nico Huber has uploaded this change for review.

View Change

Implement IFWI layout reading

Alas, yet another conflicting command-line option before we overhaul the
internal layout handling.

Change-Id: I1f1ef4c5d109b81dec7a11bca04ba89204fa84eb
Signed-off-by: Nico Huber <nico.h@gmx.de>
---
M Makefile
M cli_classic.c
A ifwi.c
M libflashrom.c
M libflashrom.h
5 files changed, 463 insertions(+), 3 deletions(-)

git pull ssh://review.coreboot.org:29418/flashrom refs/changes/18/31018/1
diff --git a/Makefile b/Makefile
index 1e7db3e..4a1094d 100644
--- a/Makefile
+++ b/Makefile
@@ -536,7 +536,7 @@
###############################################################################
# Library code.

-LIB_OBJS = libflashrom.o layout.o flashrom.o udelay.o programmer.o helpers.o ich_descriptors.o fmap.o
+LIB_OBJS = libflashrom.o layout.o flashrom.o udelay.o programmer.o helpers.o ich_descriptors.o fmap.o ifwi.o

###############################################################################
# Frontend related stuff.
diff --git a/cli_classic.c b/cli_classic.c
index 536ef24..f764a6d 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -57,6 +57,7 @@
" --fmap read ROM layout from fmap embedded in ROM\n"
" --fmap-file <fmapfile> read ROM layout from fmap in <fmapfile>\n"
" --ifd read layout from an Intel Firmware Descriptor\n"
+ " --ifwi read layout from an Integrated Firmware Image\n"
" -i | --image <name> only flash image <name> from flash layout\n"
" -o | --output <logfile> log output to <logfile>\n"
" --flash-contents <ref-file> assume flash contents to be <ref-file>\n"
@@ -100,7 +101,7 @@
struct flashctx *fill_flash;
const char *name;
int namelen, opt, i, j;
- int startchip = -1, chipcount = 0, option_index = 0, force = 0, ifd = 0, fmap = 0;
+ int startchip = -1, chipcount = 0, option_index = 0, force = 0, ifd = 0, ifwi = 0, fmap = 0;
#if CONFIG_PRINT_WIKI == 1
int list_supported_wiki = 0;
#endif
@@ -110,6 +111,7 @@
enum programmer prog = PROGRAMMER_INVALID;
enum {
OPTION_IFD = 0x0100,
+ OPTION_IFWI,
OPTION_FMAP,
OPTION_FMAP_FILE,
OPTION_FLASH_CONTENTS,
@@ -129,6 +131,7 @@
{"force", 0, NULL, 'f'},
{"layout", 1, NULL, 'l'},
{"ifd", 0, NULL, OPTION_IFD},
+ {"ifwi", 0, NULL, OPTION_IFWI},
{"fmap", 0, NULL, OPTION_FMAP},
{"fmap-file", 1, NULL, OPTION_FMAP_FILE},
{"image", 1, NULL, 'i'},
@@ -238,6 +241,10 @@
fprintf(stderr, "Error: --layout and --ifd both specified. Aborting.\n");
cli_classic_abort_usage();
}
+ if (ifwi) {
+ fprintf(stderr, "Error: --layout and --ifwi both specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
if (fmap) {
fprintf(stderr, "Error: --layout and --fmap-file both specified. Aborting.\n");
cli_classic_abort_usage();
@@ -249,12 +256,31 @@
fprintf(stderr, "Error: --layout and --ifd both specified. Aborting.\n");
cli_classic_abort_usage();
}
+ if (ifwi) {
+ fprintf(stderr, "Error: --ifwi and --ifd both specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
if (fmap) {
fprintf(stderr, "Error: --fmap-file and --ifd both specified. Aborting.\n");
cli_classic_abort_usage();
}
ifd = 1;
break;
+ case OPTION_IFWI:
+ if (layoutfile) {
+ fprintf(stderr, "Error: --layout and --ifwi both specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ if (ifd) {
+ fprintf(stderr, "Error: --ifd and --ifwi both specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ if (fmap) {
+ fprintf(stderr, "Error: --fmap-file and --ifwi both specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
+ ifwi = 1;
+ break;
case OPTION_FMAP_FILE:
if (fmap) {
fprintf(stderr, "Error: --fmap or --fmap-file specified "
@@ -265,6 +291,10 @@
fprintf(stderr, "Error: --fmap-file and --ifd both specified. Aborting.\n");
cli_classic_abort_usage();
}
+ if (ifwi) {
+ fprintf(stderr, "Error: --fmap-file and --ifwi both specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
if (layoutfile) {
fprintf(stderr, "Error: --fmap-file and --layout both specified. Aborting.\n");
cli_classic_abort_usage();
@@ -282,6 +312,10 @@
fprintf(stderr, "Error: --fmap and --ifd both specified. Aborting.\n");
cli_classic_abort_usage();
}
+ if (ifwi) {
+ fprintf(stderr, "Error: --fmap and --ifwi both specified. Aborting.\n");
+ cli_classic_abort_usage();
+ }
if (layoutfile) {
fprintf(stderr, "Error: --layout and --fmap both specified. Aborting.\n");
cli_classic_abort_usage();
@@ -452,7 +486,7 @@
goto out;
}

- if (!ifd && !fmap && process_include_args(get_global_layout())) {
+ if (!ifd && !ifwi && !fmap && process_include_args(get_global_layout())) {
ret = 1;
goto out;
}
@@ -611,6 +645,10 @@
process_include_args(layout))) {
ret = 1;
goto out_shutdown;
+ } else if (ifwi && (flashrom_layout_read_from_ifwi(&layout, fill_flash) ||
+ process_include_args(layout))) {
+ ret = 1;
+ goto out_shutdown;
} else if (fmap && fmapfile) {
struct stat s;
if (stat(fmapfile, &s) != 0) {
diff --git a/ifwi.c b/ifwi.c
new file mode 100644
index 0000000..320335a
--- /dev/null
+++ b/ifwi.c
@@ -0,0 +1,359 @@
+/*
+ * This file is part of the flashrom project.
+ *
+ * Copyright (C) 2019 secunet Security Networks AG
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "hwaccess.h"
+#include "layout.h"
+#include "flash.h"
+#include "ifwi.h"
+
+static size_t bpdt_size(const struct bpdt *const header)
+{
+ return BPDT_HEADER_LENGTH + header->desc_count * BPDT_ENTRY_LENGTH;
+}
+
+static int deserialize_bpdt_header(struct bpdt *const header, const uint8_t *const from)
+{
+ header->signature = read_le32(from + 0);
+ header->desc_count = read_le16(from + 4);
+ header->version = read_le16(from + 6);
+ header->xorsum = read_le32(from + 8);
+ header->ifwi_version = read_le32(from + 12);
+
+ if (header->signature != BPDT_SIGNATURE) {
+ msg_gerr("IFWI: BPDT signature mismatch. Found 0x%08"PRIx32", expected 0x%08x.\n",
+ header->signature, BPDT_SIGNATURE);
+ return -4;
+ }
+ if (header->version != 1) {
+ msg_gerr("IFWI: Unknown BPDT version: %"PRIu32".\n", header->version);
+ return -3;
+ }
+ return 0;
+}
+
+static int deserialize_bpdt_entries(struct bpdt *const bpdt, const uint8_t *const from, const size_t limit)
+{
+ unsigned int i;
+ size_t offset;
+
+ for (i = 0, offset = 0; i < bpdt->desc_count; ++i, offset += BPDT_ENTRY_LENGTH) {
+ bpdt->entries[i].type = read_le16(from + offset + 0);
+ bpdt->entries[i].flags = read_le16(from + offset + 2);
+ bpdt->entries[i].offset = read_le32(from + offset + 4);
+ bpdt->entries[i].size = read_le32(from + offset + 8);
+ msg_gdbg("IFWI: BPDT entry #%u (0x%04"PRIx16", 0x%04"PRIx16", 0x%08"PRIx32", 0x%08"PRIx32")\n",
+ i, bpdt->entries[i].type, bpdt->entries[i].flags,
+ bpdt->entries[i].offset, bpdt->entries[i].size);
+
+ const uint32_t end = bpdt->entries[i].offset + bpdt->entries[i].size;
+ if (end < bpdt->entries[i].offset) {
+ msg_gerr("IFWI: BPDT entry overflows 32-bit space; "
+ "offset 0x%08"PRIx32" size 0x%08"PRIx32"\n",
+ bpdt->entries[i].offset, bpdt->entries[i].size);
+ return -4;
+ }
+ if (end > limit) {
+ msg_gerr("IFWI: BPDT entry points outside of the partition: %08"PRIx32":%08"PRIx32"\n",
+ bpdt->entries[i].offset, end - 1);
+ return -4;
+ }
+ }
+ return (int)i;
+}
+
+static int read_bpdt(struct bpdt **bpdt,
+ const size_t flash_offset, const size_t partition_limit,
+ int (*const read)(void *args, uint8_t *buf, unsigned int start, unsigned int len),
+ void *const read_args)
+{
+ uint8_t *buf;
+ int ret;
+
+ buf = malloc(BPDT_HEADER_LENGTH);
+ *bpdt = malloc(sizeof(**bpdt));
+ if (!buf || !*bpdt) {
+ msg_gerr("Out of memory!\n");
+ ret = -1;
+ goto _free_bpdt_ret;
+ }
+
+ if (read(read_args, buf, flash_offset, BPDT_HEADER_LENGTH)) {
+ ret = -2;
+ goto _free_bpdt_ret;
+ }
+ ret = deserialize_bpdt_header(*bpdt, buf);
+ if (ret < 0)
+ goto _free_bpdt_ret;
+
+ buf = realloc_or_free(buf, bpdt_size(*bpdt));
+ *bpdt = realloc_or_free(*bpdt, offsetof(struct bpdt, entries) +
+ (*bpdt)->desc_count * sizeof((*bpdt)->entries[0]));
+ if (!buf || !*bpdt) {
+ ret = -1;
+ goto _free_bpdt_ret;
+ }
+
+ if (read(read_args, buf + BPDT_HEADER_LENGTH,
+ flash_offset + BPDT_HEADER_LENGTH, bpdt_size(*bpdt) - BPDT_HEADER_LENGTH)) {
+ ret = -2;
+ goto _free_bpdt_ret;
+ }
+ ret = deserialize_bpdt_entries(*bpdt, (uint8_t *)buf + BPDT_HEADER_LENGTH, partition_limit);
+ if (ret < 0)
+ goto _free_bpdt_ret;
+
+ ret = 0;
+ goto _free_buf_ret;
+
+_free_bpdt_ret:
+ free(*bpdt);
+_free_buf_ret:
+ free(buf);
+ return ret;
+}
+
+static size_t spd_size(const struct spd *const header)
+{
+ return header->header_length + header->num_entries * SPD_ENTRY_LENGTH;
+}
+
+static int deserialize_spd_header(struct spd *const header, const uint8_t *const from)
+{
+ header->marker = read_le32(from + 0);
+ header->num_entries = read_le32(from + 4);
+ header->header_version = read_le8 (from + 8);
+ header->entry_version = read_le8 (from + 9);
+ header->header_length = read_le8 (from + 10);
+ header->checksum = read_le8 (from + 11);
+ memcpy(header->name, from + 12, sizeof(header->name) - 1);
+ header->name[sizeof(header->name) - 1] = '\0';
+
+ if (header->marker != SPD_MARKER) {
+ msg_gerr("IFWI: SPD marker mismatch. Found 0x%08"PRIx32", expected 0x%08x.\n",
+ header->marker, SPD_MARKER);
+ return -4;
+ }
+ if (header->header_version != 1) {
+ msg_gerr("IFWI: Unknown SPD header version: %"PRIu8".\n", header->header_version);
+ return -3;
+ }
+ if (header->entry_version != 1) {
+ msg_gerr("IFWI: Unknown SPD entry version: %"PRIu8".\n", header->entry_version);
+ return -3;
+ }
+ return 0;
+}
+
+static int deserialize_spd_entries(struct spd *const spd, const uint8_t *const from, const size_t limit)
+{
+ unsigned int i;
+ size_t offset;
+
+ for (i = 0, offset = 0; i < spd->num_entries; ++i, offset += SPD_ENTRY_LENGTH) {
+ memcpy(spd->entries[i].name, from + offset, sizeof(spd->entries[i].name) - 1);
+ spd->entries[i].name[sizeof(spd->entries[i].name) - 1] = '\0';
+ spd->entries[i].offset = read_le32(from + offset + 12);
+ spd->entries[i].length = read_le32(from + offset + 16);
+ msg_gdbg("IFWI: SPD entry #%u (%s, 0x%08"PRIx32", 0x%08"PRIx32")\n",
+ i, spd->entries[i].name, spd->entries[i].offset, spd->entries[i].length);
+
+ const uint32_t end = spd->entries[i].offset + spd->entries[i].length;
+ if (end < spd->entries[i].offset) {
+ msg_gerr("IFWI: SPD entry overflows 32-bit space; "
+ "offset 0x%08"PRIx32" size 0x%08"PRIx32"\n",
+ spd->entries[i].offset, spd->entries[i].length);
+ return -4;
+ }
+ if (end > limit) {
+ msg_gerr("IFWI: SPD entry points outside of the partition: %08"PRIx32":%08"PRIx32"\n",
+ spd->entries[i].offset, end - 1);
+ return -4;
+ }
+ }
+ return (int)i;
+}
+
+static int read_spd(struct spd **spd,
+ const size_t flash_offset, const size_t partition_limit,
+ int (*const read)(void *args, uint8_t *buf, unsigned int start, unsigned int len),
+ void *const read_args)
+{
+ uint8_t *buf;
+ int ret;
+
+ buf = malloc(SPD_MIN_HEADER_LENGTH);
+ *spd = malloc(sizeof(**spd));
+ if (!buf || !*spd) {
+ msg_gerr("Out of memory!\n");
+ ret = -1;
+ goto _free_spd_ret;
+ }
+
+ if (read(read_args, buf, flash_offset, SPD_MIN_HEADER_LENGTH)) {
+ ret = -2;
+ goto _free_spd_ret;
+ }
+ ret = deserialize_spd_header(*spd, (uint8_t *)buf);
+ if (ret < 0)
+ goto _free_spd_ret;
+
+ buf = realloc_or_free(buf, spd_size(*spd));
+ *spd = realloc_or_free(*spd, offsetof(struct spd, entries) +
+ (*spd)->num_entries * sizeof((*spd)->entries[0]));
+ if (!buf || !*spd) {
+ ret = -1;
+ goto _free_spd_ret;
+ }
+
+ if (read(read_args, buf + (*spd)->header_length,
+ flash_offset + (*spd)->header_length, spd_size(*spd) - (*spd)->header_length)) {
+ ret = -2;
+ goto _free_spd_ret;
+ }
+ ret = deserialize_spd_entries(*spd, (uint8_t *)buf + (*spd)->header_length, partition_limit);
+ if (ret < 0)
+ goto _free_spd_ret;
+
+ ret = 0;
+ goto _free_buf_ret;
+
+_free_spd_ret:
+ free(*spd);
+_free_buf_ret:
+ free(buf);
+ return ret;
+}
+
+static int layout_from_ifwi(struct flashrom_layout **const layout,
+ const size_t primary_offset, const size_t secondary_offset,
+ const size_t partition_limit,
+ int (*const read)(void *args, uint8_t *buf, unsigned int start, unsigned int len),
+ void *const read_args)
+{
+ struct bpdt *bpdt;
+ struct spd *spd;
+ size_t bpdt_offset, offset;
+ unsigned int i, j;
+ int ret = -1;
+
+ if (!secondary_offset)
+ bpdt_offset = primary_offset;
+ else
+ bpdt_offset = secondary_offset;
+
+ ret = read_bpdt(&bpdt, bpdt_offset, partition_limit - primary_offset, read, read_args);
+ if (ret < 0)
+ return ret;
+
+ if (!secondary_offset && !(*layout = new_layout(32))) {
+ ret = -1;
+ goto _free_bpdt_ret;
+ }
+
+ ret = add_layout_entry(layout, secondary_offset ? "S-BPDT" : "BPDT",
+ bpdt_offset, bpdt_offset + bpdt_size(bpdt) - 1);
+ if (ret < 0)
+ goto _free_layout_ret;
+
+ for (i = 0; i < bpdt->desc_count; ++i) {
+ offset = primary_offset + bpdt->entries[i].offset;
+
+ if (!bpdt->entries[i].offset || !bpdt->entries[i].size) {
+ msg_gdbg("IFWI: Skipping empty BPDT entry #%u of type %"PRIu16".\n",
+ i, bpdt->entries[i].type);
+ continue;
+ }
+
+ switch (bpdt->entries[i].type) {
+ case 5:
+ /* recurse into secondary BPDT */
+ ret = layout_from_ifwi(layout, primary_offset, offset,
+ partition_limit, read, read_args);
+ if (ret)
+ goto _free_layout_ret;
+ continue;
+ case 0: /* Fall through.*/
+ case 1: /* Fall through.*/
+ case 2: /* Fall through.*/
+ case 3: /* Fall through.*/
+ case 4: /* Fall through.*/
+ case 6: /* Fall through.*/
+ case 7: /* Fall through.*/
+ case 8: /* Fall through.*/
+ case 9: /* Fall through.*/
+ case 14: /* Fall through.*/
+ case 15:
+ break;
+ default:
+ continue;
+ }
+
+ ret = read_spd(&spd, offset, partition_limit - offset, read, read_args);
+ switch (ret) {
+ case 0:
+ break;
+ case -1: /* Fall through. */
+ case -2:
+ goto _free_layout_ret;
+ default:
+ continue;
+ }
+
+ ret = add_layout_entry(layout, spd->name, offset, offset + bpdt->entries[i].size - 1);
+ if (ret < 0) {
+ free(spd);
+ goto _free_layout_ret;
+ }
+ for (j = 0; j < spd->num_entries; ++j) {
+ offset = primary_offset + bpdt->entries[i].offset +
+ (spd->entries[j].offset & SPD_ENTRY_OFFSET_MASK);
+ ret = add_layout_entry(
+ layout, spd->entries[j].name,
+ offset, offset + spd->entries[j].length - 1);
+ if (ret < 0) {
+ free(spd);
+ goto _free_layout_ret;
+ }
+ }
+ free(spd);
+ }
+
+ ret = 0;
+ goto _free_bpdt_ret;
+
+_free_layout_ret:
+ free(*layout);
+_free_bpdt_ret:
+ free(bpdt);
+ return ret;
+}
+
+int layout_from_ifwi_rom(struct flashrom_layout **const layout,
+ struct flashrom_flashctx *const flashctx,
+ const size_t flash_offset)
+{
+ return layout_from_ifwi(layout, flash_offset, 0, flashctx->chip->total_size * 1024,
+ (int (*)(void *, uint8_t *, unsigned int, unsigned int))flashctx->chip->read,
+ flashctx);
+}
diff --git a/libflashrom.c b/libflashrom.c
index f90a22c..96e34cc 100644
--- a/libflashrom.c
+++ b/libflashrom.c
@@ -30,6 +30,7 @@
#include "layout.h"
#include "hwaccess.h"
#include "ich_descriptors.h"
+#include "ifwi.h"
#include "libflashrom.h"

/**
@@ -383,6 +384,67 @@
#endif
}

+/**
+ * @brief Read a layout from the Intel IFWI in the flash.
+ *
+ * @param[out] layout Points to a struct flashrom_layout pointer that
+ * gets set if the descriptor is read and parsed
+ * successfully.
+ * @param[in] flashctx Flash context to read the descriptor from flash.
+ *
+ * @return 0 on success,
+ * 6 if descriptor parsing isn't implemented for the host,
+ * 3 if the data on flash couldn't be parsed,
+ * 2 if the data on flash couldn't be read,
+ * 1 on any other error.
+ */
+int flashrom_layout_read_from_ifwi(struct flashrom_layout **const layout,
+ struct flashrom_flashctx *const flashctx)
+{
+ struct flashrom_layout *ifd_layout;
+ unsigned int i;
+ int ret;
+
+ ret = flashrom_layout_read_from_ifd(&ifd_layout, flashctx, NULL, 0);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ifd_layout->num_entries; ++i) {
+ if (strcmp(ifd_layout->entries[i].name, "bios") == 0)
+ break;
+ }
+ if (i >= ifd_layout->num_entries) {
+ msg_gerr("Cannot find BIOS region in Intel Firmware Descriptor (IFD).\n");
+ ret = 3;
+ goto _free_ifd_ret;
+ }
+
+ msg_cinfo("Reading IFWI layout... ");
+ ret = layout_from_ifwi_rom(layout, flashctx, ifd_layout->entries[i].start);
+ switch (ret) {
+ case -4: /* Fall through. */
+ case -3:
+ ret = 3;
+ break;
+ case -2:
+ ret = 2;
+ break;
+ case 0:
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+ if (ret)
+ msg_cinfo("FAILED.\n");
+ else
+ msg_cinfo("done.\n");
+
+_free_ifd_ret:
+ free(ifd_layout);
+ return ret;
+}
+
static int flashrom_layout_parse_fmap(struct flashrom_layout **layout,
struct flashctx *const flashctx, const struct fmap *const fmap)
{
diff --git a/libflashrom.h b/libflashrom.h
index 38c95d2..9ca7ea0 100644
--- a/libflashrom.h
+++ b/libflashrom.h
@@ -63,6 +63,7 @@

struct flashrom_layout;
int flashrom_layout_read_from_ifd(struct flashrom_layout **, struct flashrom_flashctx *, const void *dump, size_t len);
+int flashrom_layout_read_from_ifwi(struct flashrom_layout **, struct flashrom_flashctx *);
int flashrom_layout_read_fmap_from_rom(struct flashrom_layout **,
struct flashrom_flashctx *, off_t offset, size_t length);
int flashrom_layout_read_fmap_from_buffer(struct flashrom_layout **layout,

To view, visit change 31018. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: flashrom
Gerrit-Branch: master
Gerrit-Change-Id: I1f1ef4c5d109b81dec7a11bca04ba89204fa84eb
Gerrit-Change-Number: 31018
Gerrit-PatchSet: 1
Gerrit-Owner: Nico Huber <nico.h@gmx.de>
Gerrit-MessageType: newchange