diff --git a/cli_classic.c b/cli_classic.c index 8f3701964..f622720c7 100644 --- a/cli_classic.c +++ b/cli_classic.c @@ -108,17 +108,17 @@ static void cli_classic_usage(const char *name) printf("Usage: %s [-h|-R|-L|" "\n\t-p [:] [-c ]\n" "\t\t(--flash-name|--flash-size|\n" - "\t\t [-E|-x|(-r|-w|-v) ]\n" + "\t\t [-E|-x|(-r|-w|-v) []]\n" "\t\t [(-l |--ifd| --fmap|--fmap-file ) [-i [:]]...]\n" "\t\t [-n] [-N] [-f])]\n" "\t[-V[V[V]]] [-o ]\n\n", name); printf(" -h | --help print this help text\n" " -R | --version print version (release)\n" - " -r | --read read flash and save to \n" - " -w | --write (|-) write or the content provided\n" + " -r | --read [] read flash and save to \n" + " -w | --write [|-] write or the content provided\n" " on the standard input to flash\n" - " -v | --verify (|-) verify flash against \n" + " -v | --verify [|-] verify flash against \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 @@ static int write_buf_to_include_args(const struct flashrom_layout *const layout, 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 @@ static int do_write(struct flashctx *const flash, const char *const filename, co } /* 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 @@ static int do_verify(struct flashctx *const flash, const char *const filename) } /* 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 @@ static void parse_options(int argc, char **argv, const char *optstring, 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 @@ static void parse_options(int argc, char **argv, const char *optstring, 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 main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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 @@ int main(int argc, char *argv[]) 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 47df6c795..4f2e68e85 100644 --- a/doc/classic_cli_manpage.rst +++ b/doc/classic_cli_manpage.rst @@ -14,7 +14,7 @@ SYNOPSIS | **flashrom** [-h|-R|-L| | -p [:] [-c ] | (--flash-name|--flash-size| -| [-E|-x|-r |-w |-v ] +| [-E|-x|-r []|-w []|-v []] | [(-l |--ifd|--fmap|--fmap-file ) | [-i [:]]] | [--wp-status] [--wp-list] [--wp-enable|--wp-disable] @@ -50,10 +50,13 @@ Also you are advised to make a backup of your current ROM contents with ``-r`` b All operations involving any chip access (probe/read/write/...) require the ``-p/--programmer`` option to be used (please see below). -**-r, --read ** +**-r, --read []** Read flash ROM contents and save them into the given ****. If the file already exists, it will be overwritten. + The **** 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 (|-)** Write **** into flash ROM. If **-** is provided instead, contents will be read from stdin. @@ -64,6 +67,9 @@ All operations involving any chip access (probe/read/write/...) require the ``-p 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 **** 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 @@ All operations involving any chip access (probe/read/write/...) require the ``-p Verify the flash ROM contents against the given ****. If **-** is provided instead, contents will be written to the stdout. + The **** 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 @@ All operations involving any chip access (probe/read/write/...) require the ``-p **-i, --include [:]** - Read or write only **** 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 **** 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 **** 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 **** parameter following those operations becomes optional and will be ignored + if present whenever the 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 **** into region-sized files **foo.bin** and **bar.bin**, run:: - flashrom -p prog -l -i foo:foo.bin -i bar:bar.bin -r rom.bin + flashrom -p prog -r -l -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 **** to the ROM, run:: - flashrom -p prog -l -i foo:foo.bin -i bar:bar.bin -w rom.bin + flashrom -p prog -w -l -i foo:foo.bin -i bar:bar.bin + + To verify regions named **foo** and **bar** using layout file **** and files **foo.bin** and **bar.bin**, run:: + + flashrom -p prog -v -l -i foo:foo.bin -i bar:bar.bin **--wp-status** @@ -321,18 +373,18 @@ All operations involving any chip access (probe/read/write/...) require the ``-p **--progress** - Show progress percentage of operations on the standard output. + Show progress percentage of operations on the standard output. **--sacrifice-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 @@ Example:: 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 d03a15b20..ce3dd4ba2 100644 --- a/include/layout.h +++ b/include/layout.h @@ -69,6 +69,7 @@ int layout_from_file(struct flashrom_layout **, const char *name); 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 e46e61ae5..a1aa9647e 100644 --- a/layout.c +++ b/layout.c @@ -288,6 +288,19 @@ int process_include_args(struct flashrom_layout *l, const struct layout_include_ 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) {