Nico Huber has uploaded this change for review. ( https://review.coreboot.org/c/flashrom/+/31018
Change subject: Implement IFWI layout reading ......................................................................
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,
Nico Huber has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/31018 )
Change subject: Implement IFWI layout reading ......................................................................
Patch Set 1:
Not sure if we want this upstream at all... it's not even about NOR flash. Just an Intel specific storage format.
Paul Menzel has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/31018 )
Change subject: Implement IFWI layout reading ......................................................................
Patch Set 1: Code-Review+1
(1 comment)
https://review.coreboot.org/#/c/31018/1//COMMIT_MSG Commit Message:
https://review.coreboot.org/#/c/31018/1//COMMIT_MSG@11 PS1, Line 11: Can you please add an example command line?
Hello Paul Menzel, Thomas Heijligen, build bot (Jenkins),
I'd like you to reexamine a change. Please visit
https://review.coreboot.org/c/flashrom/+/31018
to look at the new patch set (#2).
Change subject: Implement IFWI layout reading ......................................................................
Implement IFWI layout reading
Alas, yet another conflicting command-line option before we overhaul the internal layout handling.
v2: Also parse the descriptors of Logical Boot Partitio 2. Prefix region names with `LBP1/` and `LBP2/` respectively.
v3: Split entries out of the structs `bpdt` and `spd`. Having them not in one structure makes the code a little simpler.
Change-Id: I1f1ef4c5d109b81dec7a11bca04ba89204fa84eb Signed-off-by: Nico Huber nico.h@gmx.de --- M Makefile M cli_classic.c A ifwi.c M ifwi.h M libflashrom.c M libflashrom.h 6 files changed, 485 insertions(+), 4 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/18/31018/2
David Hendricks has uploaded a new patch set (#3) to the change originally created by Nico Huber. ( https://review.coreboot.org/c/flashrom/+/31018 )
Change subject: Implement IFWI layout reading ......................................................................
Implement IFWI layout reading
Alas, yet another conflicting command-line option before we overhaul the internal layout handling.
v2: Also parse the descriptors of Logical Boot Partitio 2. Prefix region names with `LBP1/` and `LBP2/` respectively.
v3: Split entries out of the structs `bpdt` and `spd`. Having them not in one structure makes the code a little simpler.
Change-Id: I1f1ef4c5d109b81dec7a11bca04ba89204fa84eb Signed-off-by: Nico Huber nico.h@gmx.de --- M Makefile M cli_classic.c A ifwi.c M ifwi.h M libflashrom.c M libflashrom.h 6 files changed, 474 insertions(+), 4 deletions(-)
git pull ssh://review.coreboot.org:29418/flashrom refs/changes/18/31018/3
David Hendricks has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/31018 )
Change subject: Implement IFWI layout reading ......................................................................
Patch Set 3: Code-Review-1
(2 comments)
Mostly looks good, just a couple potential issues.
https://review.coreboot.org/c/flashrom/+/31018/3/ifwi.c File ifwi.c:
https://review.coreboot.org/c/flashrom/+/31018/3/ifwi.c@242 PS3, Line 242: char _name[sizeof((*layout)->entries[0].name)]; Don't you need to allocate memory and set (*layout)->entries[0].name? I see that new_layout() is called, but that only allocates size for the structs themselves, right?
https://review.coreboot.org/c/flashrom/+/31018/3/ifwi.c@368 PS3, Line 368: free(*layout); flashrom_layout_release(*layout)?
David Hendricks has posted comments on this change. ( https://review.coreboot.org/c/flashrom/+/31018 )
Change subject: Implement IFWI layout reading ......................................................................
Patch Set 3:
(1 comment)
https://review.coreboot.org/c/flashrom/+/31018/3/Makefile File Makefile:
https://review.coreboot.org/c/flashrom/+/31018/3/Makefile@563 PS3, Line 563: LIB_OBJS = libflashrom.o layout.o flashrom.o udelay.o programmer.o helpers.o ich_descriptors.o fmap.o ifwi.o Also, remember to update meson.build