Anastasia Klimchuk submitted this change.

View Change

Approvals: Anastasia Klimchuk: Looks good to me, approved build bot (Jenkins): Verified
cli_classic.c: Make -r/-w/-v argument optional when using -i

Make the filename parameter directly following -r/-w/-v optional, since
the -i parameter allows the image to be written to be sourced from
multiple files, regions to be read from flash and written to separate
image files, and regions to be verified using an image file only
containing that region.

Since the filename parameter following -w/-v was ignored when a
filename was specified following `-i <region>:<filename>`, this patch
essentially removes the requirement to provide an unused parameter.

Based on https://review.coreboot.org/c/flashrom/+/52362.

TEST=run the following commands on a supported board:
flashrom -p internal -r /tmp/coreboot.rom
flashrom -p internal -r --ifd -i bios:/tmp/coreboot.rom
flashrom -p internal -r /tmp/coreboot.rom --ifd -i bios:/tmp/bios.bin
flashrom -p internal -w /tmp/coreboot.rom
flashrom -p internal -w --ifd -i bios:/tmp/coreboot.rom
flashrom -p internal -w /tmp/coreboot.rom --ifd -i bios:/tmp/bios.bin
flashrom -p internal -v /tmp/coreboot.rom
flashrom -p internal -v --ifd -i bios:/tmp/coreboot.rom
flashrom -p internal -v /tmp/coreboot.rom --ifd -i bios:/tmp/bios.bin

Change-Id: I6eba095d478f1a7bdbc3854627a656f93dd9e452
Signed-off-by: Matt DeVillier <matt.devillier@gmail.com>
Reviewed-on: https://review.coreboot.org/c/flashrom/+/85159
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Anastasia Klimchuk <aklm@chromium.org>
---
M cli_classic.c
M doc/classic_cli_manpage.rst
M include/layout.h
M layout.c
4 files changed, 176 insertions(+), 38 deletions(-)

diff --git a/cli_classic.c b/cli_classic.c
index 8f37019..f622720 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -108,17 +108,17 @@
printf("Usage: %s [-h|-R|-L|"
"\n\t-p <programmername>[:<parameters>] [-c <chipname>]\n"
"\t\t(--flash-name|--flash-size|\n"
- "\t\t [-E|-x|(-r|-w|-v) <file>]\n"
+ "\t\t [-E|-x|(-r|-w|-v) [<file>]]\n"
"\t\t [(-l <layoutfile>|--ifd| --fmap|--fmap-file <file>) [-i <region>[:<file>]]...]\n"
"\t\t [-n] [-N] [-f])]\n"
"\t[-V[V[V]]] [-o <logfile>]\n\n", name);

printf(" -h | --help print this help text\n"
" -R | --version print version (release)\n"
- " -r | --read <file> read flash and save to <file>\n"
- " -w | --write (<file>|-) write <file> or the content provided\n"
+ " -r | --read [<file>] read flash and save to <file>\n"
+ " -w | --write [<file>|-] write <file> or the content provided\n"
" on the standard input to flash\n"
- " -v | --verify (<file>|-) verify flash against <file>\n"
+ " -v | --verify [<file>|-] verify flash against <file>\n"
" or the content provided on the standard input\n"
" -E | --erase erase flash memory\n"
" -V | --verbose more verbose output\n"
@@ -616,6 +616,23 @@
return 0;
}

+static char *get_optional_filename(char *argv[])
+{
+ char *filename = NULL;
+
+ /* filename was supplied in optarg (i.e. -rfilename) */
+ if (optarg != NULL)
+ filename = strdup(optarg);
+ /* filename is on optind if it is not another flag (i.e. -r filename)
+ * - is treated as stdin, so we still strdup in this case
+ */
+ else if (optarg == NULL && argv[optind] != NULL &&
+ (argv[optind][0] != '-' || argv[optind][1] == '\0'))
+ filename = strdup(argv[optind++]);
+
+ return filename;
+}
+
static int do_read(struct flashctx *const flash, const char *const filename)
{
int ret;
@@ -663,8 +680,10 @@
}

/* Read '-w' argument first... */
- if (read_buf_from_file(newcontents, flash_size, filename))
- goto _free_ret;
+ if (filename) {
+ if (read_buf_from_file(newcontents, flash_size, filename))
+ goto _free_ret;
+ }
/*
* ... then update newcontents with contents from files provided to '-i'
* args if needed.
@@ -697,8 +716,10 @@
}

/* Read '-v' argument first... */
- if (read_buf_from_file(newcontents, flash_size, filename))
- goto _free_ret;
+ if (filename) {
+ if (read_buf_from_file(newcontents, flash_size, filename))
+ goto _free_ret;
+ }
/*
* ... then update newcontents with contents from files provided to '-i'
* args if needed.
@@ -773,12 +794,12 @@
switch (opt) {
case 'r':
cli_classic_validate_singleop(&operation_specified);
- options->filename = strdup(optarg);
+ options->filename = get_optional_filename(argv);
options->read_it = true;
break;
case 'w':
cli_classic_validate_singleop(&operation_specified);
- options->filename = strdup(optarg);
+ options->filename = get_optional_filename(argv);
options->write_it = true;
break;
case 'v':
@@ -787,7 +808,7 @@
if (options->dont_verify_it) {
cli_classic_abort_usage("--verify and --noverify are mutually exclusive. Aborting.\n");
}
- options->filename = strdup(optarg);
+ options->filename = get_optional_filename(argv);
options->verify_it = true;
break;
case 'n':
@@ -1033,12 +1054,12 @@
int ret = 0;

struct cli_options options = { 0 };
- static const char optstring[] = "r:Rw:v:nNVEfc:l:i:p:Lzho:x";
+ static const char optstring[] = "r::Rw::v::nNVEfc:l:i:p:Lzho:x";
static const struct option long_options[] = {
- {"read", 1, NULL, 'r'},
- {"write", 1, NULL, 'w'},
+ {"read", 2, NULL, 'r'},
+ {"write", 2, NULL, 'w'},
{"erase", 0, NULL, 'E'},
- {"verify", 1, NULL, 'v'},
+ {"verify", 2, NULL, 'v'},
{"noverify", 0, NULL, 'n'},
{"noverify-all", 0, NULL, 'N'},
{"extract", 0, NULL, 'x'},
@@ -1098,7 +1119,7 @@

parse_options(argc, argv, optstring, long_options, &options);

- if ((options.read_it | options.write_it | options.verify_it) && check_filename(options.filename, "image"))
+ if (options.filename && check_filename(options.filename, "image"))
cli_classic_abort_usage(NULL);
if (options.layoutfile && check_filename(options.layoutfile, "layout"))
cli_classic_abort_usage(NULL);
@@ -1316,6 +1337,57 @@
goto out_shutdown;
}

+ /*
+ * Common rules for -r/-w/-v syntax parsing:
+ *
+ * - If no filename is specified at all, quit.
+ *
+ * - If a file is specified for -r/-w/-v and no files are specified with
+ * -i args (or -i is not used), then that file will be used for reading/
+ * writing/verifying the entire ROM.
+ *
+ * - If no filename is specified for -r/-w/-v, but files are specified
+ * for -i, then the number of file arguments for -i options must be
+ * equal to the total number of -i options.
+ *
+ * Rules for reading:
+ *
+ * - If files are specified for -i args but not -r, do partial reads for
+ * each -i arg, creating a new file for each region. Each -i option
+ * must specify a filename.
+ *
+ * - If filenames are specified for -r and -i args, then:
+ * - Do partial read for each -i arg, creating a new file for
+ * each region where a filename is provided (-i region:filename).
+ * - Create a ROM-sized file with partially filled content. For each
+ * -i arg, fill the corresponding offset with content from ROM.
+ *
+ * Rules for writing and verifying:
+ *
+ * - If files are specified for both -w/-v and -i args, -i files take
+ * priority (files specified for -w/-v are unused).
+ *
+ * - If files are specified for -i args but not -w, do partial writes
+ * for each -i arg. Likewise for -v and -i args. All -i args must
+ * supply a filename. Any omission is considered ambiguous.
+ *
+ * - Regions with a filename associated must not overlap. This is also
+ * considered ambiguous. Note: This is checked later since it requires
+ * processing the layout/fmap first.
+ */
+ if ((options.read_it | options.write_it | options.verify_it) && !options.filename) {
+ if (!options.include_args) {
+ msg_gerr("Error: No image file specified.\n");
+ ret = 1;
+ goto out_shutdown;
+ }
+
+ if (check_include_args_filename(options.include_args)) {
+ ret = 1;
+ goto out_shutdown;
+ }
+ }
+
if (options.flash_name) {
if (fill_flash->chip->vendor && fill_flash->chip->name) {
printf("vendor=\"%s\" name=\"%s\"\n",
diff --git a/doc/classic_cli_manpage.rst b/doc/classic_cli_manpage.rst
index 47df6c7..4f2e68e 100644
--- a/doc/classic_cli_manpage.rst
+++ b/doc/classic_cli_manpage.rst
@@ -14,7 +14,7 @@
| **flashrom** [-h|-R|-L|
| -p <programmername>[:<parameters>] [-c <chipname>]
| (--flash-name|--flash-size|
-| [-E|-x|-r <file>|-w <file>|-v <file>]
+| [-E|-x|-r [<file>]|-w [<file>]|-v [<file>]]
| [(-l <file>|--ifd|--fmap|--fmap-file <file>)
| [-i <include>[:<file>]]]
| [--wp-status] [--wp-list] [--wp-enable|--wp-disable]
@@ -50,10 +50,13 @@
All operations involving any chip access (probe/read/write/...) require the ``-p/--programmer`` option to be used (please see below).


-**-r, --read <file>**
+**-r, --read [<file>]**
Read flash ROM contents and save them into the given **<file>**.
If the file already exists, it will be overwritten.

+ The **<file>** parameter is required here unless reading is restricted to one or more flash regions via the ``-i/--include`` parameter
+ and the file is specified there. See the ``--include`` section below for examples.
+

**-w, --write (<file>|-)**
Write **<file>** into flash ROM. If **-** is provided instead, contents will be read from stdin.
@@ -64,6 +67,9 @@
This copy is updated along with the write operation. In case of erase errors it is even re-read completely.
After writing has finished and if verification is enabled, the whole flash chip is read out and compared with the input image.

+ The **<file>** parameter is required here unless writing is restricted to one or more flash regions via the ``-i/--include`` parameter
+ and the file is specified there. See the ``--include`` section below for examples.
+

**-n, --noverify**
Skip the automatic verification of flash ROM contents after writing. Using this option is **not** recommended,
@@ -91,6 +97,9 @@
Verify the flash ROM contents against the given **<file>**.
If **-** is provided instead, contents will be written to the stdout.

+ The **<file>** parameter is required here unless verification is restricted to one or more flash regions via the ``-i/--include`` parameter
+ and the file is specified there. See the ``--include`` section below for examples.
+

**-E, --erase**
Erase the flash ROM chip.
@@ -187,23 +196,66 @@


**-i, --include <region>[:<file>]**
- Read or write only **<region>** to or from ROM.
- The **-i** option may be used multiple times if the user wishes to read or write multiple regions using a single command.
+ Read, write, or verify only **<region>** to or from ROM.
+ The **-i** option may be used multiple times if the user wishes to read, write, or verify multiple regions using a single command.

The user may optionally specify a corresponding **<file>** for any region they wish to read or write.
A read operation will read the corresponding regions from ROM and write individual files for each one.
A write option will read file(s) and write to the corresponding region(s) in ROM.

- For write operations, files specified using ``-i`` take precedence over content from the argument to ``-w``.
+ For all read/write/verify operations, the **<file>** parameter following those operations becomes optional and will be ignored
+ if present whenever the <file> is specified following the region.
+
+ Common rules for -r/-w/-v syntax parsing:
+
+ - If no filename is specified at all, quit.
+
+ - If a file is specified for -r/-w/-v and no files are specified with
+ -i args (or -i is not used), then that file will be used for reading/
+ writing/verifying the entire ROM.
+
+ - If no filename is specified for -r/-w/-v, but files are specified
+ for -i, then the number of file arguments for -i options must be
+ equal to the total number of -i options.
+
+ Rules for reading:
+
+ - If files are specified for -i args but not -r, do partial reads for
+ each -i arg, creating a new file for each region. Each -i option
+ must specify a filename.
+
+ - If filenames are specified for -r and -i args, then:
+
+ - Do partial read for each -i arg, creating a new file for
+ each region where a filename is provided (-i region:filename).
+ - Create a ROM-sized file with partially filled content. For each
+ -i arg, fill the corresponding offset with content from ROM.
+
+ Rules for writing and verifying:
+
+ - If files are specified for both -w/-v and -i args, -i files take
+ priority (files specified for -w/-v are unused).
+
+ - If files are specified for -i args but not -w, do partial writes
+ for each -i arg. Likewise for -v and -i args. All -i args must
+ supply a filename. Any omission is considered ambiguous.
+
+ - Regions with a filename associated must not overlap. This is also
+ considered ambiguous. Note: This is checked later since it requires
+ processing the layout/fmap first.

Examples:
To read regions named **foo** and **bar** in layout file **<layout>** into region-sized files **foo.bin** and **bar.bin**, run::

- flashrom -p prog -l <layout> -i foo:foo.bin -i bar:bar.bin -r rom.bin
+ flashrom -p prog -r -l <layout> -i foo:foo.bin -i bar:bar.bin

To write files **foo.bin** and **bar.bin** into regions named **foo** and **bar** in layout file **<layout>** to the ROM, run::

- flashrom -p prog -l <layout> -i foo:foo.bin -i bar:bar.bin -w rom.bin
+ flashrom -p prog -w -l <layout> -i foo:foo.bin -i bar:bar.bin
+
+ To verify regions named **foo** and **bar** using layout file **<layout>** and files **foo.bin** and **bar.bin**, run::
+
+ flashrom -p prog -v -l <layout> -i foo:foo.bin -i bar:bar.bin


**--wp-status**
@@ -321,18 +373,18 @@


**--progress**
- Show progress percentage of operations on the standard output.
+ Show progress percentage of operations on the standard output.

**--sacrifice-ratio <ratio>**
- Fraction (as a percentage, 0-50) of an erase block that may be erased even if unmodified.
- Larger values may complete programming faster, but may also hurt chip longevity by erasing cells unnecessarily.
+ Fraction (as a percentage, 0-50) of an erase block that may be erased even if unmodified.
+ Larger values may complete programming faster, but may also hurt chip longevity by erasing cells unnecessarily.

- Default is 0, S+1 size block only selected if all the S size blocks inside it need to be erased in full.
- 50 means that if more than a half of the area needs to be erased,
- a S+1 size block can be selected to cover all the area with one erase.
- The tradeoff is the speed of programming operation VS the longevity of the chip. Default is longevity.
+ Default is 0, S+1 size block only selected if all the S size blocks inside it need to be erased in full.
+ 50 means that if more than a half of the area needs to be erased,
+ a S+1 size block can be selected to cover all the area with one erase.
+ The tradeoff is the speed of programming operation VS the longevity of the chip. Default is longevity.

- DANGEROUS! It wears your chip faster!
+ DANGEROUS! It wears your chip faster!


**-R, --version**
@@ -707,16 +759,16 @@
write-protected (on real hardware the pin is usually negated, but not here).

**Frequency**
- Frequency can be specified in ``Hz`` (default), ``KHz``, or ``MHz`` (not case sensitive).
- If ``freq`` parameter is passed in from command line, commands will delay for certain time before returning,
- so that to emulate the requested frequency.
+ Frequency can be specified in ``Hz`` (default), ``KHz``, or ``MHz`` (not case sensitive).
+ If ``freq`` parameter is passed in from command line, commands will delay for certain time before returning,
+ so that to emulate the requested frequency.

- Valid range is [1Hz, 8000Mhz] and there is no delay by default.
+ Valid range is [1Hz, 8000Mhz] and there is no delay by default.

- The delay of an SPI command is proportional to the number of bits send over SPI bus in both directions
- and is calculated based on the assumption that we transfer at 1 bit/Hz::
+ The delay of an SPI command is proportional to the number of bits send over SPI bus in both directions
+ and is calculated based on the assumption that we transfer at 1 bit/Hz::

- flashrom -p dummy:emulate=W25Q128FV,freq=64mhz
+ flashrom -p dummy:emulate=W25Q128FV,freq=64mhz


nic3com, nicrealtek, nicnatsemi, nicintel, nicintel_eeprom, nicintel_spi, gfxnvidia, ogp_spi, drkaiser, satasii, satamv, atahpt, atavia, atapromise, it8212 programmers
diff --git a/include/layout.h b/include/layout.h
index d03a15b..ce3dd4b 100644
--- a/include/layout.h
+++ b/include/layout.h
@@ -69,6 +69,7 @@

int register_include_arg(struct layout_include_args **, const char *arg);
int process_include_args(struct flashrom_layout *, const struct layout_include_args *);
+int check_include_args_filename(const struct layout_include_args *);
void cleanup_include_args(struct layout_include_args **);

const struct romentry *layout_next_included_region(const struct flashrom_layout *, chipoff_t);
diff --git a/layout.c b/layout.c
index e46e61a..a1aa964 100644
--- a/layout.c
+++ b/layout.c
@@ -288,6 +288,19 @@
return 0;
}

+int check_include_args_filename(const struct layout_include_args *include_args)
+{
+ const struct layout_include_args *arg;
+ for (arg = include_args; arg; arg = arg->next) {
+ if (!arg->file || (arg->file[0] == '\0')) {
+ fprintf(stderr, "Error: No region file specified for -i/--include option.\n");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/* returns boolean 1 if any regions overlap, 0 otherwise */
int included_regions_overlap(const struct flashrom_layout *const l)
{

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

Gerrit-MessageType: merged
Gerrit-Project: flashrom
Gerrit-Branch: main
Gerrit-Change-Id: I6eba095d478f1a7bdbc3854627a656f93dd9e452
Gerrit-Change-Number: 85159
Gerrit-PatchSet: 9
Gerrit-Owner: Matt DeVillier <matt.devillier@gmail.com>
Gerrit-Reviewer: Anastasia Klimchuk <aklm@chromium.org>
Gerrit-Reviewer: build bot (Jenkins) <no-reply@coreboot.org>