Thomas Heijligen has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/57265 )
Change subject: layout/fmap: reimplement for endian independence ......................................................................
layout/fmap: reimplement for endian independence
Change-Id: I6d38649327d2021a9917d1294f1541376ed6717d Signed-off-by: Thomas Heijligen thomas.heijligen@secunet.de --- M fmap.c M layout.h M libflashrom.c 3 files changed, 177 insertions(+), 375 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/65/57265/1
diff --git a/fmap.c b/fmap.c index b18cbf7..903ffc2 100644 --- a/fmap.c +++ b/fmap.c @@ -1,331 +1,158 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + /* - * Copyright 2010, Google LLC. - * Copyright 2018-present, Facebook Inc. - * All rights reserved. + * This file is part of flashrom project. For the full license text see LICENSE.txt. * - * 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. + * Copyright (C) 2021, secunet Secure Networks AG, + * written by Thomas Heijligen thomas.heijligen@secunet.com */
-#include <ctype.h> -#include <stdlib.h> +/* + * This file contais functions to find and read layout information from a fmap structure. + */ + +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> #include <string.h> -#include <sys/types.h> -#include "flash.h" -#include "fmap.h" +#include "layout.h" +#include "libflashrom.h" +#include "hwaccess.h"
-static size_t fmap_size(const struct fmap *fmap) +#define FMAP_SIGNATURE "__FMAP__" +#define FMAP_NAME_SIZE 32 +#define FMAP_VERSION_MAJOR 1 +#define FMAP_VERSION_MINOR 1 + +#define FMAP_HEADER_SIZE 56 +#define FMAP_HEADER_OFFSET_SIGNATURE 0 +#define FMAP_HEADER_OFFSET_VER_MAJOR 8 +#define FMAP_HEADER_OFFSET_VER_MINOR 9 +#define FMAP_HEADER_OFFSET_BASE 10 +#define FMAP_HEADER_OFFSET_SIZE 18 +#define FMAP_HEADER_OFFSET_NAME 22 +#define FMAP_HEADER_OFFSET_NAREAS 54 + +#define FMAP_AREA_SIZE 42 +#define FMAP_AREA_OFFSET_OFFSET 0 +#define FMAP_AERA_OFFSET_SIZE 4 +#define FMAP_AREA_OFFSET_NAME 8 +#define FMAP_AREA_OFFSET_FLAGS 40 + +/** + * @brief find the signature of a FMAP in a buffer, uses linear search + * @param[in] buffer + * @param[in] buffer_size + * @param[out] fmap_offset, on SUCCESS the offset where to find the fmap, if NULL or FAILURE don't change + * @return 0 SUCCESS + * 1 UNEXPECTED_NULL_POINTER + * 2 NOT_FOUND - FMAP signature not found + */ +int find_fmap_in_buffer(const void *const buffer, const uint64_t buffer_size, uint64_t *const fmap_offset) { - return sizeof(*fmap) + (fmap->nareas * sizeof(struct fmap_area)); -} + /* test for unexpected NULL pointer */ + if (buffer == NULL) { + return 1; /* UNEXPECTED_NULL_POINTER */ + }
-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; + for (uint64_t offset = 0; (offset + FMAP_HEADER_SIZE) < buffer_size; offset++) { + if (memcmp(buffer + offset + FMAP_HEADER_OFFSET_SIGNATURE, FMAP_SIGNATURE, strlen(FMAP_SIGNATURE)) == 0) { + if (fmap_offset != NULL) { + *fmap_offset = offset; + } + return 0; /* SUCCESS */ } } - return 1; - + return 2; /* NOT_FOUND */ }
/** - * @brief Do a brute-force linear search for fmap in provided buffer + * TODO: implement * - * @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 + * @brief find the signature of a FMAP on the flash chip + * @param[in] flashctx + * @param[out] fmap_offset on SUCCESS offset of the FMAP, else not changed + * @param[out] fmap_size on SUCCESS size of the FMAP, else not changed + * @return 0 SUCESS + * 2 NOT_FOUND - FMAP signature not found */ -static off_t fmap_lsearch(const uint8_t *buf, size_t len) +int find_fmap_in_rom(const struct flashrom_flashctx *const flashctx, uint64_t *const fmap_offset, uint64_t *const fmap_size) { - off_t offset; - bool fmap_found = 0; + return 2; +}
- for (offset = 0; offset <= (off_t)(len - sizeof(struct fmap)); offset++) { - if (is_valid_fmap((struct fmap *)&buf[offset])) { - fmap_found = 1; - break; + +/** + * @brief read out the layout information from a FMAP in buffer + * @param[out] layout + * @param[in] buffer + * @param[in] buffer_size + * @param[in] fmap_offset + * @return 0 SUCCESS + * 1 UNEXPECTED_NULL_POINTER + * 3 BUFFER_TO_SMALL + * 4 INVALID_SIGNATURE + * 5 INVALID_VERSION + * 6 MEMORY_ALLOCATION_FAILED + */ +int read_layout_from_fmap(struct flashrom_layout **layout, const void *const buffer, const uint64_t buffer_size, const uint64_t fmap_offset) +{ + /* buffer must not be NULL */ + if (buffer == NULL) { + return 1; /* FLASHROM_UNEXPECTED_NULL_POINTER */ + } + /* buffer_size must be larger than fmap_offset + FMAP_HEADER_SIZE */ + if (buffer_size < fmap_offset + FMAP_HEADER_SIZE) { + return 3; /* FLASHROM_BUFFER_TO_SMALL */ + } + /* fmap signature check */ + if (memcmp(buffer + fmap_offset + FMAP_HEADER_OFFSET_SIGNATURE, FMAP_SIGNATURE, strlen(FMAP_SIGNATURE)) != 0) { + return 4; /* FLASHROM_INVALID_SIGNATURE */ + } + /* fmap version check */ + if ((read_le8(buffer + fmap_offset + FMAP_HEADER_OFFSET_VER_MAJOR) != FMAP_VERSION_MAJOR) && + (read_le8(buffer + fmap_offset + FMAP_HEADER_OFFSET_VER_MINOR) != FMAP_VERSION_MINOR)) { + return 5; /* FLASHROM_INVALID_VERSION */ + } + + const void* const fmap = buffer + fmap_offset; + + struct flashrom_layout *l = NULL; + if (flashrom_layout_new(&l) != 0) { + return 6; /* FLASHROM_MEMORY_ALLOCATION_FAILED */ + } + uint64_t firmware_base = read_le64(fmap + FMAP_HEADER_OFFSET_BASE); + /* uint32_t firmware_size = read_le32(fmap + FMAP_HEADER_OFFSET_SIZE); TODO: mode checks */ + char firmware_name[FMAP_NAME_SIZE + 1] = {'\0'}; + memcpy(firmware_name, fmap + FMAP_HEADER_OFFSET_NAME, FMAP_NAME_SIZE); + uint16_t firmware_nareas = read_le16(fmap + FMAP_HEADER_OFFSET_NAREAS); + + /* check if buffer includes all fmap areas */ + if (buffer_size < (fmap_offset + FMAP_HEADER_SIZE + (firmware_nareas * FMAP_AREA_SIZE))) { + return 3; /* FLASHROM_BUFFER_TO_SMALL */ + } + + for (unsigned int i = 0; i < firmware_nareas; i++) { + const void* const region = fmap + FMAP_HEADER_SIZE + (i * FMAP_AREA_SIZE); + + uint32_t area_offset = read_le32(region + FMAP_AREA_OFFSET_OFFSET); + uint32_t area_size = read_le32(region + FMAP_AERA_OFFSET_SIZE); + char area_name[FMAP_NAME_SIZE + 1] = {'\0'}; + char combined_name[(2 * FMAP_NAME_SIZE) + 2]; + memcpy(area_name, region + FMAP_AREA_OFFSET_NAME, FMAP_NAME_SIZE); + snprintf(combined_name, sizeof(combined_name), "%s_%s", firmware_name, area_name); + + int ret = flashrom_layout_add_region(l, + firmware_base + area_offset, + firmware_base + area_offset + area_size, + combined_name ); + + if (ret != 0) { + flashrom_layout_release(l); + return 6; /* FLASHROM_MEMORY_ALLOCATION_FAILED */ } }
- 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; - } - 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, size_t 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(*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; - } - 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; -} + *layout = l; + return 0; /* FLASHROM_SUCCESS */ +} \ No newline at end of file diff --git a/layout.h b/layout.h index 713241f..6e084ae 100644 --- a/layout.h +++ b/layout.h @@ -68,4 +68,8 @@ void prepare_layout_for_extraction(struct flashrom_flashctx *); int layout_sanity_checks(const struct flashrom_flashctx *);
+int find_fmap_in_buffer(const void *const buffer, const uint64_t buffer_size, uint64_t *const fmap_offset); +int find_fmap_in_rom(const struct flashrom_flashctx *const flashctx, uint64_t *const fmap_offset, uint64_t *const fmap_size); +int read_layout_from_fmap(struct flashrom_layout **layout, const void *const buffer, const uint64_t buffer_size, const uint64_t fmap_offset); + #endif /* !__LAYOUT_H__ */ diff --git a/libflashrom.c b/libflashrom.c index 1ecf650..ae8cd21 100644 --- a/libflashrom.c +++ b/libflashrom.c @@ -495,31 +495,6 @@ #endif }
-#ifdef __FLASHROM_LITTLE_ENDIAN__ -static int flashrom_layout_parse_fmap(struct flashrom_layout **layout, - struct flashctx *const flashctx, const struct fmap *const fmap) -{ - int i; - char name[FMAP_STRLEN + 1]; - const struct fmap_area *area; - struct flashrom_layout *l; - - if (!fmap || flashrom_layout_new(&l)) - return 1; - - for (i = 0, area = fmap->areas; i < fmap->nareas; i++, area++) { - snprintf(name, sizeof(name), "%s", area->name); - if (flashrom_layout_add_region(l, area->offset, area->offset + area->size - 1, name)) { - flashrom_layout_release(l); - return 1; - } - } - - *layout = l; - return 0; -} -#endif /* __FLASHROM_LITTLE_ENDIAN__ */ - /** * @brief Read a layout by searching the flash chip for fmap. * @@ -537,27 +512,35 @@ 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; + (void)(offset); // TODO: remove unused parameter
- 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; + int ret; + uint64_t fmap_offset, fmap_size; + + if(flashctx == NULL || flashctx->chip == NULL || flashctx->chip->read == NULL) { + return 2; // UNEXPECTED_NULL_POINTER }
- 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; + ret = find_fmap_in_rom(flashctx, &fmap_offset, &fmap_size); + if (ret != 0) { + return ret; } + void *fmap_buffer = malloc(fmap_size); + if (fmap_buffer == NULL) { + return 1; // MEMORY_ALLOCATION_FAILED + } + // prepare flash access? + // use flashrom_image_read? + ret = flashctx->chip->read(flashctx, fmap_buffer, fmap_offset, fmap_size); + if (ret != 0) { + free(fmap_buffer); + return ret; + } + ret = read_layout_from_fmap(layout, fmap_buffer, fmap_size, 0); + free(fmap_buffer);
- free(fmap); + // TODO add layout to flashctx? return ret; -#endif }
/** @@ -577,33 +560,21 @@ 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; + (void)(flashctx); // Unused + int ret; + uint64_t fmap_offset;
- 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; + ret = find_fmap_in_buffer(buf, size, &fmap_offset); + if (ret != 0) { + return ret; + } + ret = read_layout_from_fmap(layout, buf, size, fmap_offset); + if( ret != 0) { + return 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 + // TODO add layout to flashctx? + return 0; }
/**