diff --git a/cli_classic.c b/cli_classic.c index e39496101..7a7125b0f 100644 --- a/cli_classic.c +++ b/cli_classic.c @@ -33,6 +33,7 @@ enum { OPTION_IFD = 0x0100, OPTION_FMAP, OPTION_FMAP_FILE, + OPTION_FMAP_VERIFY, OPTION_FLASH_CONTENTS, OPTION_FLASH_NAME, OPTION_FLASH_SIZE, @@ -65,7 +66,7 @@ struct cli_options { const struct programmer_entry *prog; char *pparam; - bool ifd, fmap; + bool ifd, fmap, fmap_verify; struct flashrom_layout *layout; struct layout_include_args *include_args; char *layoutfile; @@ -132,6 +133,7 @@ static void cli_classic_usage(const char *name) " --flash-size read out the detected flash size\n" " --fmap read ROM layout from fmap embedded in ROM\n" " --fmap-file read ROM layout from fmap in \n" + " --fmap-verify read ROM layout from fmap and verify it matches file fmap\n" " --ifd read layout from an Intel Firmware Descriptor\n" " -i | --include [:] only read/write image from layout\n" " (optionally with data from )\n" @@ -847,7 +849,7 @@ static void parse_options(int argc, char **argv, const char *optstring, break; case OPTION_FMAP_FILE: if (options->fmap) - cli_classic_abort_usage("Error: --fmap or --fmap-file specified " + cli_classic_abort_usage("Error: --fmap, --fmap-file, or --fmap-verify specified " "more than once. Aborting.\n"); if (options->ifd) cli_classic_abort_usage("Error: --fmap-file and --ifd both specified. Aborting.\n"); @@ -858,7 +860,7 @@ static void parse_options(int argc, char **argv, const char *optstring, break; case OPTION_FMAP: if (options->fmap) - cli_classic_abort_usage("Error: --fmap or --fmap-file specified " + cli_classic_abort_usage("Error: --fmap, --fmap-file, or --fmap-verify specified " "more than once. Aborting.\n"); if (options->ifd) cli_classic_abort_usage("Error: --fmap and --ifd both specified. Aborting.\n"); @@ -866,6 +868,19 @@ static void parse_options(int argc, char **argv, const char *optstring, cli_classic_abort_usage("Error: --layout and --fmap both specified. Aborting.\n"); options->fmap = true; break; + case OPTION_FMAP_VERIFY: + if (options->fmap) + cli_classic_abort_usage("Error: --fmap, --fmap-file, or --fmap-verify specified " + "more than once. Aborting.\n"); + if (options->ifd) + cli_classic_abort_usage("Error: --fmap-verify and --ifd both specified. Aborting.\n"); + if (options->layoutfile) + cli_classic_abort_usage("Error: --fmap-verify and --layout both specified. Aborting.\n"); + if (options->read_it || options->verify_it) + cli_classic_abort_usage("Error: --fmap-verify cannot be used with read or verify operations. Aborting.\n"); + options->fmap = true; + options->fmap_verify = true; + break; case 'i': if (register_include_arg(&options->include_args, optarg)) cli_classic_abort_usage(NULL); @@ -1060,6 +1075,7 @@ int main(int argc, char *argv[]) {"ifd", 0, NULL, OPTION_IFD}, {"fmap", 0, NULL, OPTION_FMAP}, {"fmap-file", 1, NULL, OPTION_FMAP_FILE}, + {"fmap-verify", 0, NULL, OPTION_FMAP_VERIFY}, {"image", 1, NULL, 'i'}, // (deprecated): back compatibility. {"include", 1, NULL, 'i'}, {"flash-contents", 1, NULL, OPTION_FLASH_CONTENTS}, @@ -1436,11 +1452,54 @@ int main(int argc, char *argv[]) goto out_shutdown; } free(fmapfile_buffer); - } else if (options.fmap && (flashrom_layout_read_fmap_from_rom(&options.layout, &context, 0, + } else if (options.fmap) { + /* Read layout from ROM fmap */ + if (flashrom_layout_read_fmap_from_rom(&options.layout, &context, 0, flashrom_flash_getsize(&context)) || - process_include_args(options.layout, options.include_args))) { - ret = 1; - goto out_shutdown; + process_include_args(options.layout, options.include_args)) { + ret = 1; + goto out_shutdown; + } + if (options.fmap_verify) { + struct flashrom_layout *file_layout = NULL; + struct stat s; + if (stat(options.filename, &s) != 0) { + msg_gerr("Failed to stat the file \"%s\"\n", options.filename); + ret = 1; + goto out_release; + } + + size_t fmapfile_size = s.st_size; + uint8_t *file_buffer = malloc(fmapfile_size); + if (!file_buffer) { + ret = 1; + goto out_release; + } + + if (read_buf_from_file(file_buffer, fmapfile_size, options.filename)) { + ret = 1; + free(file_buffer); + goto out_release; + } + /* Read layout from file fmap */ + if (flashrom_layout_read_fmap_from_buffer(&file_layout, &context, file_buffer, + flashrom_flash_getsize(&context)) || + process_include_args(file_layout, options.include_args)) { + ret = 1; + free(file_buffer); + goto out_release; + } + free(file_buffer); + /* compare the two layouts */ + if (flashrom_layout_compare(options.layout, file_layout)) { + msg_cerr("FMAP layouts do not match! Aborting.\n"); + flashrom_layout_release(file_layout); + ret = 1; + goto out_release; + } + flashrom_layout_release(file_layout); + msg_cinfo("FMAP layouts match.\n"); + } } flashrom_layout_set(&context, options.layout); diff --git a/doc/classic_cli_manpage.rst b/doc/classic_cli_manpage.rst index 74cb2bbab..82f7582c2 100644 --- a/doc/classic_cli_manpage.rst +++ b/doc/classic_cli_manpage.rst @@ -15,7 +15,7 @@ SYNOPSIS | -p [:] [-c ] | (--flash-name|--flash-size| | [-E|-x|-r []|-w []|-v []] -| [(-l |--ifd|--fmap|--fmap-file ) +| [(-l |--ifd|--fmap|--fmap-file |--fmap-verify) | [-i [:]]] | [--wp-status] [--wp-list] [--wp-enable|--wp-disable] | [--wp-range ,|--wp-region ] @@ -179,6 +179,21 @@ All operations involving any chip access (probe/read/write/...) require the ``-p flashrom -p prog --fmap-file some.rom --image COREBOOT -w some.rom +**--fmap-verify** + Read ROM layout from fmap embedded in the ROM and verify that it matches the fmap in the file to be written. + + This option is mutually exclusive with **--fmap**, **--fmap-file**, **--layout**, and **--ifd**. + It can only be used with write operations, as it does not make sense otherwise. Before writing, **flashrom** + will read the fmap from both the flash chip and the file to be written, then compare them. If the fmaps do + not match, the write operation will be aborted. + + This is useful to prevent accidentally writing a ROM image with an incompatible layout to a flash chip. + + If you only want to update the **COREBOOT** region defined in the fmap, and verify that the fmap matches, run:: + + flashrom -p prog --fmap-verify -w new.rom --image COREBOOT + + **--ifd** Read ROM layout from Intel Firmware Descriptor. diff --git a/doc/release_notes/devel.rst b/doc/release_notes/devel.rst index 74824c27d..5eab95d24 100644 --- a/doc/release_notes/devel.rst +++ b/doc/release_notes/devel.rst @@ -38,6 +38,27 @@ If you are flashing multiple regions or ones that partially overlap with read-only parts then that could result in flashrom failing in the middle, leaving you in unknown state. +FMAP verification support +-------------------------- + +A new ``--fmap-verify`` option has been added that allows verification of FMAP +(Flash Map) layout compatibility before performing write operations. This option +reads the FMAP layout from both the flash ROM and the file to be written, then +compares them to ensure they match before proceeding with the write. + +This is particularly useful when updating specific regions (e.g., COREBOOT) to +prevent accidentally writing a ROM image with an incompatible layout that could +result in a bricked system. The ``--fmap-verify`` option is mutually exclusive +with ``--fmap``, ``--fmap-file``, ``--layout``, and ``--ifd`` options, and can +only be used with write operations (``-w``). + +Example usage:: + + flashrom -p programmer --fmap-verify -w newimage.rom --image COREBOOT + +If the FMAP layouts don't match, the operation will abort with a detailed error +message showing which regions differ. + New programmers =============== diff --git a/util/flashrom.bash-completion.tmpl b/util/flashrom.bash-completion.tmpl index 7b1b98ed1..f356708da 100644 --- a/util/flashrom.bash-completion.tmpl +++ b/util/flashrom.bash-completion.tmpl @@ -53,6 +53,7 @@ _flashrom() --flash-size --fmap --fmap-file + --fmap-verify --ifd --include --output