1
0
mirror of https://review.coreboot.org/flashrom.git synced 2025-04-27 23:22:37 +02:00

Add support to get layout from fmap (e.g. coreboot rom)

Flashmap, or simply fmap, is a binary data format for describing
region offsets, sizes, and certain attributes and is widely used by
coreboot. This patch adds support for the fmap data format version 1.1
and adds --fmap and --fmap-file arguments.

Using --fmap will make flashrom to search the ROM content for fmap
data. Using --fmap-file will make flashrom search a supplied file
for fmap data.

An example of how to update the COREBOOT region of a ROM:
flashrom -p programmer --fmap -w coreboot.rom -i COREBOOT
flashrom -p programmer --fmap-file coreboot.rom -w coreboot.rom -i COREBOOT

The fmap functions are mostly copied from cbfstool.

Currently it is made mutually exclusive with other layout options until
we are more clever about this input.

Change-Id: I0e7fad38ed79a84d41358e1f175c36d255786c12
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Signed-off-by: David Hendricks <dhendricks@fb.com>
Reviewed-on: https://review.coreboot.org/23203
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Tested-by: David Hendricks <david.hendricks@gmail.com>
Reviewed-by: Werner Zeh <werner.zeh@siemens.com>
Reviewed-by: Nico Huber <nico.h@gmx.de>
This commit is contained in:
Arthur Heymans 2018-01-10 12:48:16 +01:00 committed by Nico Huber
parent 4acc3f3a89
commit c82900b661
7 changed files with 648 additions and 10 deletions

View File

@ -536,7 +536,7 @@ CHIP_OBJS = jedec.o stm50.o w39.o w29ee011.o \
############################################################################### ###############################################################################
# Library code. # Library code.
LIB_OBJS = libflashrom.o layout.o flashrom.o udelay.o programmer.o helpers.o ich_descriptors.o LIB_OBJS = libflashrom.o layout.o flashrom.o udelay.o programmer.o helpers.o ich_descriptors.o fmap.o
############################################################################### ###############################################################################
# Frontend related stuff. # Frontend related stuff.

View File

@ -25,6 +25,7 @@
#include <getopt.h> #include <getopt.h>
#include "flash.h" #include "flash.h"
#include "flashchips.h" #include "flashchips.h"
#include "fmap.h"
#include "programmer.h" #include "programmer.h"
#include "libflashrom.h" #include "libflashrom.h"
@ -53,6 +54,8 @@ static void cli_classic_usage(const char *name)
" -n | --noverify don't auto-verify\n" " -n | --noverify don't auto-verify\n"
" -N | --noverify-all verify included regions only (cf. -i)\n" " -N | --noverify-all verify included regions only (cf. -i)\n"
" -l | --layout <layoutfile> read ROM layout from <layoutfile>\n" " -l | --layout <layoutfile> read ROM layout from <layoutfile>\n"
" --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" " --ifd read layout from an Intel Firmware Descriptor\n"
" -i | --image <name> only flash image <name> from flash layout\n" " -i | --image <name> only flash image <name> from flash layout\n"
" -o | --output <logfile> log output to <logfile>\n" " -o | --output <logfile> log output to <logfile>\n"
@ -97,7 +100,7 @@ int main(int argc, char *argv[])
struct flashctx *fill_flash; struct flashctx *fill_flash;
const char *name; const char *name;
int namelen, opt, i, j; int namelen, opt, i, j;
int startchip = -1, chipcount = 0, option_index = 0, force = 0, ifd = 0; int startchip = -1, chipcount = 0, option_index = 0, force = 0, ifd = 0, fmap = 0;
#if CONFIG_PRINT_WIKI == 1 #if CONFIG_PRINT_WIKI == 1
int list_supported_wiki = 0; int list_supported_wiki = 0;
#endif #endif
@ -107,6 +110,8 @@ int main(int argc, char *argv[])
enum programmer prog = PROGRAMMER_INVALID; enum programmer prog = PROGRAMMER_INVALID;
enum { enum {
OPTION_IFD = 0x0100, OPTION_IFD = 0x0100,
OPTION_FMAP,
OPTION_FMAP_FILE,
OPTION_FLASH_CONTENTS, OPTION_FLASH_CONTENTS,
}; };
int ret = 0; int ret = 0;
@ -124,6 +129,8 @@ int main(int argc, char *argv[])
{"force", 0, NULL, 'f'}, {"force", 0, NULL, 'f'},
{"layout", 1, NULL, 'l'}, {"layout", 1, NULL, 'l'},
{"ifd", 0, NULL, OPTION_IFD}, {"ifd", 0, NULL, OPTION_IFD},
{"fmap", 0, NULL, OPTION_FMAP},
{"fmap-file", 1, NULL, OPTION_FMAP_FILE},
{"image", 1, NULL, 'i'}, {"image", 1, NULL, 'i'},
{"flash-contents", 1, NULL, OPTION_FLASH_CONTENTS}, {"flash-contents", 1, NULL, OPTION_FLASH_CONTENTS},
{"list-supported", 0, NULL, 'L'}, {"list-supported", 0, NULL, 'L'},
@ -138,6 +145,7 @@ int main(int argc, char *argv[])
char *filename = NULL; char *filename = NULL;
char *referencefile = NULL; char *referencefile = NULL;
char *layoutfile = NULL; char *layoutfile = NULL;
char *fmapfile = NULL;
#ifndef STANDALONE #ifndef STANDALONE
char *logfile = NULL; char *logfile = NULL;
#endif /* !STANDALONE */ #endif /* !STANDALONE */
@ -230,6 +238,10 @@ int main(int argc, char *argv[])
fprintf(stderr, "Error: --layout and --ifd both specified. Aborting.\n"); fprintf(stderr, "Error: --layout and --ifd both specified. Aborting.\n");
cli_classic_abort_usage(); cli_classic_abort_usage();
} }
if (fmap) {
fprintf(stderr, "Error: --layout and --fmap-file both specified. Aborting.\n");
cli_classic_abort_usage();
}
layoutfile = strdup(optarg); layoutfile = strdup(optarg);
break; break;
case OPTION_IFD: case OPTION_IFD:
@ -237,8 +249,45 @@ int main(int argc, char *argv[])
fprintf(stderr, "Error: --layout and --ifd both specified. Aborting.\n"); fprintf(stderr, "Error: --layout and --ifd both specified. Aborting.\n");
cli_classic_abort_usage(); cli_classic_abort_usage();
} }
if (fmap) {
fprintf(stderr, "Error: --fmap-file and --ifd both specified. Aborting.\n");
cli_classic_abort_usage();
}
ifd = 1; ifd = 1;
break; break;
case OPTION_FMAP_FILE:
if (fmap) {
fprintf(stderr, "Error: --fmap or --fmap-file specified "
"more than once. Aborting.\n");
cli_classic_abort_usage();
}
if (ifd) {
fprintf(stderr, "Error: --fmap-file and --ifd 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();
}
fmapfile = strdup(optarg);
fmap = 1;
break;
case OPTION_FMAP:
if (fmap) {
fprintf(stderr, "Error: --fmap or --fmap-file specified "
"more than once. Aborting.\n");
cli_classic_abort_usage();
}
if (ifd) {
fprintf(stderr, "Error: --fmap and --ifd both specified. Aborting.\n");
cli_classic_abort_usage();
}
if (layoutfile) {
fprintf(stderr, "Error: --layout and --fmap both specified. Aborting.\n");
cli_classic_abort_usage();
}
fmap = 1;
break;
case 'i': case 'i':
tempstr = strdup(optarg); tempstr = strdup(optarg);
if (register_include_arg(tempstr)) { if (register_include_arg(tempstr)) {
@ -360,6 +409,9 @@ int main(int argc, char *argv[])
if (layoutfile && check_filename(layoutfile, "layout")) { if (layoutfile && check_filename(layoutfile, "layout")) {
cli_classic_abort_usage(); cli_classic_abort_usage();
} }
if (fmapfile && check_filename(fmapfile, "fmap")) {
cli_classic_abort_usage();
}
if (referencefile && check_filename(referencefile, "reference")) { if (referencefile && check_filename(referencefile, "reference")) {
cli_classic_abort_usage(); cli_classic_abort_usage();
} }
@ -399,7 +451,8 @@ int main(int argc, char *argv[])
ret = 1; ret = 1;
goto out; goto out;
} }
if (!ifd && process_include_args(get_global_layout())) {
if (!ifd && !fmap && process_include_args(get_global_layout())) {
ret = 1; ret = 1;
goto out; goto out;
} }
@ -558,6 +611,38 @@ int main(int argc, char *argv[])
process_include_args(layout))) { process_include_args(layout))) {
ret = 1; ret = 1;
goto out_shutdown; goto out_shutdown;
} else if (fmap && fmapfile) {
struct stat s;
if (stat(fmapfile, &s) != 0) {
msg_gerr("Failed to stat fmapfile \"%s\"\n", fmapfile);
ret = 1;
goto out_shutdown;
}
size_t fmapfile_size = s.st_size;
uint8_t *fmapfile_buffer = malloc(fmapfile_size);
if (!fmapfile_buffer) {
ret = 1;
goto out_shutdown;
}
if (read_buf_from_file(fmapfile_buffer, fmapfile_size, fmapfile)) {
ret = 1;
free(fmapfile_buffer);
goto out_shutdown;
}
if (flashrom_layout_read_fmap_from_buffer(&layout, fill_flash, fmapfile_buffer, fmapfile_size) ||
process_include_args(layout)) {
ret = 1;
free(fmapfile_buffer);
goto out_shutdown;
}
free(fmapfile_buffer);
} else if (fmap && (flashrom_layout_read_fmap_from_rom(&layout, fill_flash, 0,
fill_flash->chip->total_size * 1024) || process_include_args(layout))) {
ret = 1;
goto out_shutdown;
} }
flashrom_layout_set(fill_flash, layout); flashrom_layout_set(fill_flash, layout);

View File

@ -44,12 +44,10 @@
.SH NAME .SH NAME
flashrom \- detect, read, write, verify and erase flash chips flashrom \- detect, read, write, verify and erase flash chips
.SH SYNOPSIS .SH SYNOPSIS
.B flashrom \fR[\fB\-h\fR|\fB\-R\fR|\fB\-L\fR|\fB\-z\fR|\ .B flashrom \fR[\fB\-h\fR|\fB\-R\fR|\fB\-L\fR|\fB\-z\fR|\fB\-p\fR <programmername>[:<parameters>]
\fB\-p\fR <programmername>[:<parameters>] [\fB\-E\fR|\fB\-r\fR <file>|\fB\-w\fR <file>|\fB\-v\fR <file>] [\fB\-c\fR <chipname>]
[\fB\-E\fR|\fB\-r\fR <file>|\fB\-w\fR <file>|\fB\-v\fR <file>] \ [(\fB\-l\fR <file>|\fB\-\-ifd|\fB \-\-fmap\fR|\fB\-\-fmap-file\fR <file>) [\fB\-i\fR <image>]]
[\fB\-c\fR <chipname>] [\fB\-n\fR] [\fB\-N\fR] [\fB\-f\fR]]
[(\fB\-l\fR <file>|\fB\-\-ifd\fR) [\fB\-i\fR <image>]] \
[\fB\-n\fR] [\fB\-N\fR] [\fB\-f\fR]]
[\fB\-V\fR[\fBV\fR[\fBV\fR]]] [\fB-o\fR <logfile>] [\fB\-V\fR[\fBV\fR[\fBV\fR]]] [\fB-o\fR <logfile>]
.SH DESCRIPTION .SH DESCRIPTION
.B flashrom .B flashrom
@ -195,6 +193,34 @@ To update only the images named
.sp .sp
Overlapping sections are not supported. Overlapping sections are not supported.
.TP .TP
.B "\-\-fmap"
Read layout from fmap in flash chip.
.sp
flashrom supports the fmap binary format which is commonly used by coreboot
for partitioning a flash chip. The on-chip fmap will be read and used to generate
the layout.
.sp
If you only want to update the
.BR "COREBOOT"
region defined in the fmap, run
.sp
.B " flashrom -p prog \-\-fmap \-\-image COREBOOT \-w some.rom"
.TP
.B "\-\-fmap-file <file>"
Read layout from a
.BR <file>
containing binary fmap (e.g. coreboot roms).
.sp
flashrom supports the fmap binary format which is commonly used by coreboot
for partitioning a flash chip. The fmap in the specified file will be read and
used to generate the layout.
.sp
If you only want to update the
.BR "COREBOOT"
region defined in the binary fmap file, run
.sp
.B " flashrom \-p prog \-\-fmap-file some.rom \-\-image COREBOOT \-w some.rom"
.TP
.B "\-\-ifd" .B "\-\-ifd"
Read ROM layout from Intel Firmware Descriptor. Read ROM layout from Intel Firmware Descriptor.
.sp .sp

333
fmap.c Normal file
View File

@ -0,0 +1,333 @@
/*
* Copyright 2015, Google Inc.
* Copyright 2018-present, Facebook Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include "flash.h"
#include "fmap.h"
static size_t fmap_size(const struct fmap *fmap)
{
return sizeof(*fmap) + (fmap->nareas * sizeof(struct fmap_area));
}
static int is_valid_fmap(const struct fmap *fmap)
{
if (memcmp(fmap, FMAP_SIGNATURE, strlen(FMAP_SIGNATURE)) != 0)
return 0;
/* strings containing the magic tend to fail here */
if (fmap->ver_major > FMAP_VER_MAJOR)
return 0;
if (fmap->ver_minor > FMAP_VER_MINOR)
return 0;
/* a basic consistency check: flash address space size should be larger
* than the size of the fmap data structure */
if (fmap->size < fmap_size(fmap))
return 0;
/* fmap-alikes along binary data tend to fail on having a valid,
* null-terminated string in the name field.*/
int i;
for (i = 0; i < FMAP_STRLEN; i++) {
if (fmap->name[i] == 0)
break;
if (!isgraph(fmap->name[i]))
return 0;
if (i == FMAP_STRLEN - 1) {
/* name is specified to be null terminated single-word string
* without spaces. We did not break in the 0 test, we know it
* is a printable spaceless string but we're seeing FMAP_STRLEN
* symbols, which is one too many.
*/
return 0;
}
}
return 1;
}
/**
* @brief Do a brute-force linear search for fmap in provided buffer
*
* @param[in] buffer The buffer to search
* @param[in] len Length (in bytes) to search
*
* @return offset in buffer where fmap is found if successful
* -1 to indicate that fmap was not found
* -2 to indicate fmap is truncated or exceeds buffer + len
*/
static off_t fmap_lsearch(const uint8_t *buf, size_t len)
{
off_t offset;
bool fmap_found = 0;
for (offset = 0; offset <= len - sizeof(struct fmap); offset++) {
if (is_valid_fmap((struct fmap *)&buf[offset])) {
fmap_found = 1;
break;
}
}
if (!fmap_found)
return -1;
if (offset + fmap_size((struct fmap *)&buf[offset]) > len) {
msg_gerr("fmap size exceeds buffer boundary.\n");
return -2;
}
return offset;
}
/**
* @brief Read fmap from provided buffer and copy it to fmap_out
*
* @param[out] fmap_out Double-pointer to location to store fmap contents.
* Caller must free allocated fmap contents.
* @param[in] buf Buffer to search
* @param[in] len Length (in bytes) to search
*
* @return 0 if successful
* 1 to indicate error
* 2 to indicate fmap is not found
*/
int fmap_read_from_buffer(struct fmap **fmap_out, const uint8_t *const buf, size_t len)
{
off_t offset = fmap_lsearch(buf, len);
if (offset < 0) {
msg_gdbg("Unable to find fmap in provided buffer.\n");
return 2;
} else {
msg_gdbg("Found fmap at offset 0x%06zx\n", (size_t)offset);
}
const struct fmap *fmap = (const struct fmap *)(buf + offset);
*fmap_out = malloc(fmap_size(fmap));
if (*fmap_out == NULL) {
msg_gerr("Out of memory.\n");
return 1;
}
memcpy(*fmap_out, fmap, fmap_size(fmap));
return 0;
}
static int fmap_lsearch_rom(struct fmap **fmap_out,
struct flashctx *const flashctx, size_t rom_offset, size_t len)
{
int ret = -1;
uint8_t *buf;
if (prepare_flash_access(flashctx, true, false, false, false))
goto _finalize_ret;
/* likely more memory than we need, but it simplifies handling and
* printing offsets to keep them uniform with what's on the ROM */
buf = malloc(rom_offset + len);
if (!buf) {
msg_gerr("Out of memory.\n");
goto _finalize_ret;
}
ret = flashctx->chip->read(flashctx, buf + rom_offset, rom_offset, len);
if (ret) {
msg_pdbg("Cannot read ROM contents.\n");
goto _free_ret;
}
ret = fmap_read_from_buffer(fmap_out, buf + rom_offset, len);
_free_ret:
free(buf);
_finalize_ret:
finalize_flash_access(flashctx);
return ret;
}
static int fmap_bsearch_rom(struct fmap **fmap_out, struct flashctx *const flashctx,
size_t rom_offset, size_t len, int min_stride)
{
size_t stride, fmap_len = 0;
int ret = 1, fmap_found = 0, check_offset_0 = 1;
struct fmap *fmap;
const unsigned int chip_size = flashctx->chip->total_size * 1024;
const int sig_len = strlen(FMAP_SIGNATURE);
if (rom_offset + len > flashctx->chip->total_size * 1024)
return 1;
if (len < sizeof(*fmap))
return 1;
if (prepare_flash_access(flashctx, true, false, false, false))
return 1;
fmap = malloc(sizeof(struct fmap));
if (!fmap) {
msg_gerr("Out of memory.\n");
goto _free_ret;
}
/*
* For efficient operation, we start with the largest stride possible
* and then decrease the stride on each iteration. Also, check for a
* remainder when modding the offset with the previous stride. This
* makes it so that each offset is only checked once.
*
* Zero (rom_offset == 0) is a special case and is handled using a
* variable to track whether or not we've checked it.
*/
size_t offset;
for (stride = chip_size / 2; stride >= min_stride; stride /= 2) {
if (stride > len)
continue;
for (offset = rom_offset;
offset <= rom_offset + len - sizeof(struct fmap);
offset += stride) {
if ((offset % (stride * 2) == 0) && (offset != 0))
continue;
if (offset == 0 && !check_offset_0)
continue;
check_offset_0 = 0;
/* Read errors are considered non-fatal since we may
* encounter locked regions and want to continue. */
if (flashctx->chip->read(flashctx, (uint8_t *)fmap, offset, sig_len)) {
/*
* Print in verbose mode only to avoid excessive
* messages for benign errors. Subsequent error
* prints should be done as usual.
*/
msg_cdbg("Cannot read %d bytes at offset %zu\n", sig_len, offset);
continue;
}
if (memcmp(fmap, FMAP_SIGNATURE, sig_len) != 0)
continue;
if (flashctx->chip->read(flashctx, (uint8_t *)fmap + sig_len,
offset + sig_len, sizeof(*fmap) - sig_len)) {
msg_cerr("Cannot read %zu bytes at offset %06zx\n",
sizeof(*fmap) + sig_len, offset + sig_len);
continue;
}
if (is_valid_fmap(fmap)) {
msg_gdbg("fmap found at offset 0x%06zx\n", offset);
fmap_found = 1;
break;
} else {
msg_gerr("fmap signature found at %zu but header is invalid.\n", offset);
ret = 2;
}
}
if (fmap_found)
break;
}
if (!fmap_found)
goto _free_ret;
fmap_len = fmap_size(fmap);
struct fmap *tmp = fmap;
fmap = realloc(fmap, fmap_len);
if (!fmap) {
msg_gerr("Failed to realloc.\n");
free(tmp);
goto _free_ret;
}
if (flashctx->chip->read(flashctx, (uint8_t *)fmap + sizeof(*fmap),
offset + sizeof(*fmap), fmap_len - sizeof(*fmap))) {
msg_cerr("Cannot read %zu bytes at offset %06zx\n",
fmap_len - sizeof(*fmap), offset + sizeof(*fmap));
/* Treat read failure to be fatal since this
* should be a valid, usable fmap. */
ret = 2;
goto _free_ret;
}
*fmap_out = fmap;
ret = 0;
_free_ret:
if (ret)
free(fmap);
finalize_flash_access(flashctx);
return ret;
}
/**
* @brief Read fmap from ROM
*
* @param[out] fmap_out Double-pointer to location to store fmap contents.
* Caller must free allocated fmap contents.
* @param[in] flashctx Flash context
* @param[in] rom_offset Offset in ROM to begin search
* @param[in] len Length to search relative to rom_offset
*
* @return 0 on success,
* 2 if the fmap couldn't be read,
* 1 on any other error.
*/
int fmap_read_from_rom(struct fmap **fmap_out,
struct flashctx *const flashctx, size_t rom_offset, size_t len)
{
int ret;
if (!flashctx || !flashctx->chip)
return 1;
/*
* Binary search is used at first to see if we can find an fmap quickly
* in a usual location (often at a power-of-2 offset). However, once we
* reach a small enough stride the transaction overhead will reverse the
* speed benefit of using bsearch at which point we need to use brute-
* force instead.
*
* TODO: Since flashrom is often used with high-latency external
* programmers we should not be overly aggressive with bsearch.
*/
ret = fmap_bsearch_rom(fmap_out, flashctx, rom_offset, len, 256);
if (ret) {
msg_gdbg("Binary search failed, trying linear search...\n");
ret = fmap_lsearch_rom(fmap_out, flashctx, rom_offset, len);
}
return ret;
}

71
fmap.h Normal file
View File

@ -0,0 +1,71 @@
/*
* Copyright 2015, Google Inc.
* Copyright 2018-present, Facebook Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*/
#ifndef __FMAP_H__
#define __FMAP_H__ 1
#include <inttypes.h>
#include <stdbool.h>
#define FMAP_SIGNATURE "__FMAP__"
#define FMAP_VER_MAJOR 1 /* this header's FMAP minor version */
#define FMAP_VER_MINOR 1 /* this header's FMAP minor version */
#define FMAP_STRLEN 32 /* maximum length for strings */
struct fmap_area {
uint32_t offset; /* offset relative to base */
uint32_t size; /* size in bytes */
uint8_t name[FMAP_STRLEN]; /* descriptive name */
uint16_t flags; /* flags for this area */
} __attribute__((packed));
struct fmap {
uint8_t signature[8]; /* "__FMAP__" */
uint8_t ver_major; /* major version */
uint8_t ver_minor; /* minor version */
uint64_t base; /* address of the firmware binary */
uint32_t size; /* size of firmware binary in bytes */
uint8_t name[FMAP_STRLEN]; /* name of this firmware binary */
uint16_t nareas; /* number of areas described by
fmap_areas[] below */
struct fmap_area areas[];
} __attribute__((packed));
int fmap_read_from_buffer(struct fmap **fmap_out, const uint8_t *buf, size_t len);
int fmap_read_from_rom(struct fmap **fmap_out, struct flashctx *const flashctx, size_t rom_offset, size_t len);
#endif /* __FMAP_H__*/

View File

@ -25,6 +25,7 @@
#include <stdarg.h> #include <stdarg.h>
#include "flash.h" #include "flash.h"
#include "fmap.h"
#include "programmer.h" #include "programmer.h"
#include "layout.h" #include "layout.h"
#include "hwaccess.h" #include "hwaccess.h"
@ -382,6 +383,124 @@ _free_ret:
#endif #endif
} }
static int flashrom_layout_parse_fmap(struct flashrom_layout **layout,
struct flashctx *const flashctx, const struct fmap *const fmap)
{
int i;
struct flashrom_layout *l = get_global_layout();
if (!fmap || !l)
return 1;
if (l->num_entries + fmap->nareas > MAX_ROMLAYOUT) {
msg_gerr("Cannot add fmap entries to layout - Too many entries.\n");
return 1;
}
for (i = 0; i < fmap->nareas; i++) {
l->entries[l->num_entries].start = fmap->areas[i].offset;
l->entries[l->num_entries].end = fmap->areas[i].offset + fmap->areas[i].size - 1;
l->entries[l->num_entries].included = false;
memset(l->entries[l->num_entries].name, 0, sizeof(l->entries[i].name));
memcpy(l->entries[l->num_entries].name, fmap->areas[i].name,
min(FMAP_STRLEN, sizeof(l->entries[i].name)));
msg_gdbg("fmap %08x - %08x named %s\n",
l->entries[l->num_entries].start,
l->entries[l->num_entries].end,
l->entries[l->num_entries].name);
l->num_entries++;
}
*layout = l;
return 0;
}
/**
* @brief Read a layout by searching the flash chip for fmap.
*
* @param[out] layout Points to a struct flashrom_layout pointer that
* gets set if the fmap is read and parsed successfully.
* @param[in] flashctx Flash context
* @param[in] offset Offset to begin searching for fmap.
* @param[in] offset Length of address space to search.
*
* @return 0 on success,
* 3 if fmap parsing isn't implemented for the host,
* 2 if the fmap couldn't be read,
* 1 on any other error.
*/
int flashrom_layout_read_fmap_from_rom(struct flashrom_layout **const layout,
struct flashctx *const flashctx, off_t offset, size_t len)
{
#ifndef __FLASHROM_LITTLE_ENDIAN__
return 3;
#else
struct fmap *fmap = NULL;
int ret = 0;
msg_gdbg("Attempting to read fmap from ROM content.\n");
if (fmap_read_from_rom(&fmap, flashctx, offset, len)) {
msg_gerr("Failed to read fmap from ROM.\n");
return 1;
}
msg_gdbg("Adding fmap layout to global layout.\n");
if (flashrom_layout_parse_fmap(layout, flashctx, fmap)) {
msg_gerr("Failed to add fmap regions to layout.\n");
ret = 1;
}
free(fmap);
return ret;
#endif
}
/**
* @brief Read a layout by searching buffer for fmap.
*
* @param[out] layout Points to a struct flashrom_layout pointer that
* gets set if the fmap is read and parsed successfully.
* @param[in] flashctx Flash context
* @param[in] buffer Buffer to search in
* @param[in] size Size of buffer to search
*
* @return 0 on success,
* 3 if fmap parsing isn't implemented for the host,
* 2 if the fmap couldn't be read,
* 1 on any other error.
*/
int flashrom_layout_read_fmap_from_buffer(struct flashrom_layout **const layout,
struct flashctx *const flashctx, const uint8_t *const buf, size_t size)
{
#ifndef __FLASHROM_LITTLE_ENDIAN__
return 3;
#else
struct fmap *fmap = NULL;
int ret = 1;
if (!buf || !size)
goto _ret;
msg_gdbg("Attempting to read fmap from buffer.\n");
if (fmap_read_from_buffer(&fmap, buf, size)) {
msg_gerr("Failed to read fmap from buffer.\n");
goto _ret;
}
msg_gdbg("Adding fmap layout to global layout.\n");
if (flashrom_layout_parse_fmap(layout, flashctx, fmap)) {
msg_gerr("Failed to add fmap regions to layout.\n");
goto _free_ret;
}
ret = 0;
_free_ret:
free(fmap);
_ret:
return ret;
#endif
}
/** /**
* @brief Free a layout. * @brief Free a layout.
* *

View File

@ -61,8 +61,12 @@ int flashrom_image_verify(struct flashrom_flashctx *, const void *buffer, size_t
struct flashrom_layout; 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_ifd(struct flashrom_layout **, struct flashrom_flashctx *, const void *dump, size_t len);
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,
struct flashrom_flashctx *, const uint8_t *buf, size_t len);
int flashrom_layout_include_region(struct flashrom_layout *, const char *name); int flashrom_layout_include_region(struct flashrom_layout *, const char *name);
void flashrom_layout_release(struct flashrom_layout *); void flashrom_layout_release(struct flashrom_layout *);
void flashrom_layout_set(struct flashrom_flashctx *, const struct flashrom_layout *); void flashrom_layout_set(struct flashrom_flashctx *, const struct flashrom_layout *);
#endif /* !__LIBFLASHROM_H__ */ #endif /* !__LIBFLASHROM_H__ */