summaryrefslogtreecommitdiffstats
path: root/cli_classic.c
diff options
context:
space:
mode:
Diffstat (limited to 'cli_classic.c')
-rw-r--r--cli_classic.c1091
1 files changed, 770 insertions, 321 deletions
diff --git a/cli_classic.c b/cli_classic.c
index 3dbaec2b5..60f3fd508 100644
--- a/cli_classic.c
+++ b/cli_classic.c
@@ -22,15 +22,62 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>
+#include <stdbool.h>
#include <stdlib.h>
-#include <getopt.h>
+#include <cli_classic.h>
#include "flash.h"
#include "flashchips.h"
#include "fmap.h"
#include "programmer.h"
-#include "writeprotect.h"
#include "libflashrom.h"
+enum {
+ OPTION_IFD = 0x0100,
+ OPTION_FMAP,
+ OPTION_FMAP_FILE,
+ OPTION_FLASH_CONTENTS,
+ OPTION_FLASH_NAME,
+ OPTION_FLASH_SIZE,
+ OPTION_WP_STATUS,
+ OPTION_WP_SET_RANGE,
+ OPTION_WP_SET_REGION,
+ OPTION_WP_ENABLE,
+ OPTION_WP_DISABLE,
+ OPTION_WP_LIST,
+ OPTION_PROGRESS,
+};
+
+struct cli_options {
+ bool read_it, extract_it, write_it, erase_it, verify_it;
+ bool dont_verify_it, dont_verify_all;
+ bool list_supported;
+#if CONFIG_PRINT_WIKI == 1
+ bool list_supported_wiki;
+#endif
+ char *filename;
+
+ const struct programmer_entry *prog;
+ char *pparam;
+
+ bool ifd, fmap;
+ struct flashrom_layout *layout;
+ struct layout_include_args *include_args;
+ char *layoutfile;
+ char *fmapfile;
+
+ unsigned int wp_start, wp_len;
+ bool enable_wp, disable_wp, print_wp_status;
+ bool set_wp_range, set_wp_region, print_wp_ranges;
+ char *wp_region;
+
+ bool force;
+ bool flash_name, flash_size;
+ bool show_progress;
+ char *logfile;
+ char *referencefile;
+ const char *chip_to_probe;
+};
+
static void cli_classic_usage(const char *name)
{
printf("Usage: %s [-h|-R|-L|"
@@ -39,40 +86,48 @@ static void cli_classic_usage(const char *name)
#endif
"\n\t-p <programmername>[:<parameters>] [-c <chipname>]\n"
"\t\t(--flash-name|--flash-size|\n"
- "\t\t [-E|(-r|-w|-v) <file>]\n"
- "\t\t [(-l <layoutfile>|--ifd| --fmap|--fmap-file <file>) [-i <imagename>]...]\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> to flash\n"
- " -v | --verify <file> verify flash against <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"
+ " or the content provided on the standard input\n"
" -E | --erase erase flash memory\n"
" -V | --verbose more verbose output\n"
" -c | --chip <chipname> probe only for specified flash chip\n"
" -f | --force force specific operations (see man page)\n"
" -n | --noverify don't auto-verify\n"
" -N | --noverify-all verify included regions only (cf. -i)\n"
+ " -x | --extract extract regions to files\n"
" -l | --layout <layoutfile> read ROM layout from <layoutfile>\n"
" --wp-disable disable write protection\n"
" --wp-enable enable write protection\n"
- " --wp-list list write protect range\n"
- " --wp-status show write protect status\n"
- " --wp-range=<start>,<len> set write protect range\n"
+ " --wp-list list supported write protection ranges\n"
+ " --wp-status show write protection status\n"
+ " --wp-range=<start>,<len> set write protection range (use --wp-range=0,0\n"
+ " to unprotect the entire flash)\n"
+ " --wp-region <region> set write protection region\n"
" --flash-name read out the detected flash name\n"
" --flash-size read out the detected flash size\n"
" --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"
- " -i | --image <name> only flash image <name> from flash layout\n"
+ " -i | --include <region>[:<file>] only read/write image <region> from layout\n"
+ " (optionally with data from <file>)\n"
+ " --image <region>[:<file>] deprecated, please use --include\n"
" -o | --output <logfile> log output to <logfile>\n"
" --flash-contents <ref-file> assume flash contents to be <ref-file>\n"
" -L | --list-supported print supported devices\n"
#if CONFIG_PRINT_WIKI == 1
" -z | --list-supported-wiki print supported devices in wiki syntax\n"
#endif
+ " --progress show progress percentage on the standard output\n"
" -p | --programmer <name>[:<param>] specify the programmer device. One of\n");
list_programmers_linebreak(4, 80, 0);
printf(".\n\nYou can specify one of -h, -R, -L, "
@@ -105,11 +160,21 @@ static int check_filename(char *filename, const char *type)
return 1;
}
/* Not an error, but maybe the user intended to specify a CLI option instead of a file name. */
- if (filename[0] == '-')
+ if (filename[0] == '-' && filename[1] != '\0')
fprintf(stderr, "Warning: Supplied %s file name starts with -\n", type);
return 0;
}
+/* Ensure a file is open by means of fstat */
+static bool check_file(FILE *file)
+{
+ struct stat statbuf;
+
+ if (fstat(fileno(file), &statbuf) < 0)
+ return false;
+ return true;
+}
+
static int parse_wp_range(unsigned int *start, unsigned int *len)
{
char *endptr = NULL, *token = NULL;
@@ -136,98 +201,425 @@ static int parse_wp_range(unsigned int *start, unsigned int *len)
return 0;
}
-int main(int argc, char *argv[])
+static int print_wp_range(struct flashrom_flashctx *flash, size_t start, size_t len)
{
- const struct flashchip *chip = NULL;
- /* Probe for up to eight flash chips. */
- struct flashctx flashes[8] = {{0}};
- 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;
-#if CONFIG_PRINT_WIKI == 1
- int list_supported_wiki = 0;
-#endif
- int flash_name = 0, flash_size = 0;
- int set_wp_enable = 0, set_wp_disable = 0, wp_status = 0;
- int set_wp_range = 0, set_wp_region = 0, wp_list = 0;
- int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0;
- int dont_verify_it = 0, dont_verify_all = 0, list_supported = 0, operation_specified = 0;
- struct flashrom_layout *layout = NULL;
- enum programmer prog = PROGRAMMER_INVALID;
- enum {
- OPTION_IFD = 0x0100,
- OPTION_FMAP,
- OPTION_FMAP_FILE,
- OPTION_FLASH_CONTENTS,
- OPTION_FLASH_NAME,
- OPTION_FLASH_SIZE,
- OPTION_WP_STATUS,
- OPTION_WP_SET_RANGE,
- OPTION_WP_SET_REGION,
- OPTION_WP_ENABLE,
- OPTION_WP_DISABLE,
- OPTION_WP_LIST,
- };
- int ret = 0;
- unsigned int wp_start = 0, wp_len = 0;
+ /* Start address and length */
+ msg_ginfo("start=0x%08zx length=0x%08zx ", start, len);
- static const char optstring[] = "r:Rw:v:nNVEfc:l:i:p:Lzho:";
- static const struct option long_options[] = {
- {"read", 1, NULL, 'r'},
- {"write", 1, NULL, 'w'},
- {"erase", 0, NULL, 'E'},
- {"verify", 1, NULL, 'v'},
- {"noverify", 0, NULL, 'n'},
- {"noverify-all", 0, NULL, 'N'},
- {"chip", 1, NULL, 'c'},
- {"verbose", 0, NULL, 'V'},
- {"force", 0, NULL, 'f'},
- {"layout", 1, NULL, 'l'},
- {"ifd", 0, NULL, OPTION_IFD},
- {"fmap", 0, NULL, OPTION_FMAP},
- {"fmap-file", 1, NULL, OPTION_FMAP_FILE},
- {"image", 1, NULL, 'i'},
- {"flash-contents", 1, NULL, OPTION_FLASH_CONTENTS},
- {"flash-name", 0, NULL, OPTION_FLASH_NAME},
- {"flash-size", 0, NULL, OPTION_FLASH_SIZE},
- {"get-size", 0, NULL, OPTION_FLASH_SIZE}, // (deprecated): back compatibility.
- {"wp-status", 0, 0, OPTION_WP_STATUS},
- {"wp-range", required_argument, NULL, OPTION_WP_SET_RANGE},
- {"wp-region", 1, 0, OPTION_WP_SET_REGION},
- {"wp-enable", optional_argument, 0, OPTION_WP_ENABLE},
- {"wp-disable", 0, 0, OPTION_WP_DISABLE},
- {"wp-list", 0, 0, OPTION_WP_LIST},
- {"list-supported", 0, NULL, 'L'},
- {"list-supported-wiki", 0, NULL, 'z'},
- {"programmer", 1, NULL, 'p'},
- {"help", 0, NULL, 'h'},
- {"version", 0, NULL, 'R'},
- {"output", 1, NULL, 'o'},
- {NULL, 0, NULL, 0},
- };
+ /* Easily readable description like 'none' or 'lower 1/8' */
+ size_t chip_len = flashrom_flash_getsize(flash);
- char *filename = NULL;
- char *referencefile = NULL;
- char *layoutfile = NULL;
- char *fmapfile = NULL;
-#ifndef STANDALONE
- char *logfile = NULL;
-#endif /* !STANDALONE */
- char *tempstr = NULL;
- char *pparam = NULL;
- struct layout_include_args *include_args = NULL;
- char *wp_mode_opt = NULL;
+ if (len == 0) {
+ msg_ginfo("(none)");
+ } else if (len == chip_len) {
+ msg_ginfo("(all)");
+ } else {
+ const char *location = "";
+ if (start == 0)
+ location = "lower ";
+ if (start == chip_len - len)
+ location = "upper ";
- flashrom_set_log_callback((flashrom_log_callback *)&flashrom_print_cb);
+ /* Remove common factors of 2 to simplify */
+ /* the (range_len/chip_len) fraction. */
+ while ((chip_len % 2) == 0 && (len % 2) == 0) {
+ chip_len /= 2;
+ len /= 2;
+ }
- print_version();
- print_banner();
+ msg_ginfo("(%s%zu/%zu)", location, len, chip_len);
+ }
- if (selfcheck())
- exit(1);
+ return 0;
+}
+
+static const char *get_wp_error_str(int err)
+{
+ switch (err) {
+ case FLASHROM_WP_ERR_CHIP_UNSUPPORTED:
+ return "WP operations are not implemented for this chip";
+ case FLASHROM_WP_ERR_READ_FAILED:
+ return "failed to read the current WP configuration";
+ case FLASHROM_WP_ERR_WRITE_FAILED:
+ return "failed to write the new WP configuration";
+ case FLASHROM_WP_ERR_VERIFY_FAILED:
+ return "unexpected WP configuration read back from chip";
+ case FLASHROM_WP_ERR_MODE_UNSUPPORTED:
+ return "the requested protection mode is not supported";
+ case FLASHROM_WP_ERR_RANGE_UNSUPPORTED:
+ return "the requested protection range is not supported";
+ case FLASHROM_WP_ERR_RANGE_LIST_UNAVAILABLE:
+ return "could not determine what protection ranges are available";
+ case FLASHROM_WP_ERR_UNSUPPORTED_STATE:
+ return "can't operate on current WP configuration of the chip";
+ }
+ return "unknown WP error";
+}
+
+static int wp_cli(
+ struct flashctx *flash,
+ bool enable_wp,
+ bool disable_wp,
+ bool print_wp_status,
+ bool print_wp_ranges,
+ bool set_wp_range,
+ uint32_t wp_start,
+ uint32_t wp_len)
+{
+ if (print_wp_ranges) {
+ struct flashrom_wp_ranges *list;
+ enum flashrom_wp_result ret = flashrom_wp_get_available_ranges(&list, flash);
+ if (ret != FLASHROM_WP_OK) {
+ msg_gerr("Failed to get list of protection ranges: %s\n",
+ get_wp_error_str(ret));
+ return 1;
+ }
+ size_t count = flashrom_wp_ranges_get_count(list);
+
+ msg_ginfo("Available protection ranges:\n");
+ for (size_t i = 0; i < count; i++) {
+ size_t start, len;
+
+ flashrom_wp_ranges_get_range(&start, &len, list, i);
+ msg_ginfo("\t");
+ print_wp_range(flash, start, len);
+ msg_ginfo("\n");
+ }
+
+ flashrom_wp_ranges_release(list);
+ }
+
+ if (set_wp_range || disable_wp || enable_wp) {
+ enum flashrom_wp_mode old_mode = FLASHROM_WP_MODE_DISABLED;
+ struct flashrom_wp_cfg *cfg = NULL;
+ enum flashrom_wp_result ret = flashrom_wp_cfg_new(&cfg);
+
+ if (ret == FLASHROM_WP_OK)
+ ret = flashrom_wp_read_cfg(cfg, flash);
+
+ if (ret == FLASHROM_WP_OK) {
+ /* Store current WP mode for printing help text if */
+ /* changing the cfg fails. */
+ old_mode = flashrom_wp_get_mode(cfg);
+
+ if (set_wp_range)
+ flashrom_wp_set_range(cfg, wp_start, wp_len);
+
+ if (disable_wp)
+ flashrom_wp_set_mode(cfg, FLASHROM_WP_MODE_DISABLED);
+
+ if (enable_wp)
+ flashrom_wp_set_mode(cfg, FLASHROM_WP_MODE_HARDWARE);
+
+ ret = flashrom_wp_write_cfg(flash, cfg);
+ }
+
+ flashrom_wp_cfg_release(cfg);
+
+ if (ret != FLASHROM_WP_OK) {
+ msg_gerr("Failed to apply new WP settings: %s\n",
+ get_wp_error_str(ret));
+
+ /* Warn user if active WP is likely to have caused failure */
+ if (ret == FLASHROM_WP_ERR_VERIFY_FAILED) {
+ switch (old_mode) {
+ case FLASHROM_WP_MODE_HARDWARE:
+ msg_gerr("Note: hardware status register protection is enabled. "
+ "The chip's WP# pin must be set to an inactive voltage "
+ "level to be able to change the WP settings.\n");
+ break;
+ case FLASHROM_WP_MODE_POWER_CYCLE:
+ msg_gerr("Note: power-cycle status register protection is enabled. "
+ "A power-off, power-on cycle is usually required to change "
+ "the chip's WP settings.\n");
+ break;
+ case FLASHROM_WP_MODE_PERMANENT:
+ msg_gerr("Note: permanent status register protection is enabled. "
+ "The chip's WP settings cannot be modified.\n");
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 1;
+ }
+
+ if (disable_wp)
+ msg_ginfo("Disabled hardware protection\n");
+
+ if (enable_wp)
+ msg_ginfo("Enabled hardware protection\n");
+
+ if (set_wp_range) {
+ msg_ginfo("Activated protection range: ");
+ print_wp_range(flash, wp_start, wp_len);
+ msg_ginfo("\n");
+ }
+ }
+
+ if (print_wp_status) {
+ size_t start, len;
+ enum flashrom_wp_mode mode;
+ struct flashrom_wp_cfg *cfg = NULL;
+ enum flashrom_wp_result ret = flashrom_wp_cfg_new(&cfg);
+
+ if (ret == FLASHROM_WP_OK)
+ ret = flashrom_wp_read_cfg(cfg, flash);
+
+ if (ret != FLASHROM_WP_OK) {
+ msg_gerr("Failed to get WP status: %s\n",
+ get_wp_error_str(ret));
+
+ flashrom_wp_cfg_release(cfg);
+ return 1;
+ }
+
+ flashrom_wp_get_range(&start, &len, cfg);
+ mode = flashrom_wp_get_mode(cfg);
+ flashrom_wp_cfg_release(cfg);
+
+ msg_ginfo("Protection range: ");
+ print_wp_range(flash, start, len);
+ msg_ginfo("\n");
+
+ msg_ginfo("Protection mode: ");
+ switch (mode) {
+ case FLASHROM_WP_MODE_DISABLED:
+ msg_ginfo("disabled");
+ break;
+ case FLASHROM_WP_MODE_HARDWARE:
+ msg_ginfo("hardware");
+ break;
+ case FLASHROM_WP_MODE_POWER_CYCLE:
+ msg_ginfo("power_cycle");
+ break;
+ case FLASHROM_WP_MODE_PERMANENT:
+ msg_ginfo("permanent");
+ break;
+ default:
+ msg_ginfo("unknown");
+ break;
+ }
+ msg_ginfo("\n");
+ }
+
+ return 0;
+}
+
+/**
+ * @brief Reads content to buffer from one or more files.
+ *
+ * Reads content to supplied buffer from files. If a filename is specified for
+ * individual regions using the partial read syntax ('-i <region>[:<filename>]')
+ * then this will read file data into the corresponding region in the
+ * supplied buffer.
+ *
+ * @param layout The layout to be used.
+ * @param buf Chip-sized buffer to write data to
+ * @return 0 on success
+ */
+static int read_buf_from_include_args(const struct flashrom_layout *const layout, unsigned char *buf)
+{
+ const struct romentry *entry = NULL;
+
+ /*
+ * Content will be read from -i args, so they must not overlap since
+ * we need to know exactly what content to write to the ROM.
+ */
+ if (included_regions_overlap(layout)) {
+ msg_gerr("Error: Included regions must not overlap when writing.\n");
+ return 1;
+ }
+
+ while ((entry = layout_next_included(layout, entry))) {
+ if (!entry->file)
+ continue;
+ const struct flash_region *region = &entry->region;
+ if (read_buf_from_file(buf + region->start,
+ region->end - region->start + 1, entry->file))
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * @brief Writes content from buffer to one or more files.
+ *
+ * Writes content from supplied buffer to files. If a filename is specified for
+ * individual regions using the partial read syntax ('-i <region>[:<filename>]')
+ * then this will write files using data from the corresponding region in the
+ * supplied buffer.
+ *
+ * @param layout The layout to be used.
+ * @param buf Chip-sized buffer to read data from
+ * @return 0 on success
+ */
+static int write_buf_to_include_args(const struct flashrom_layout *const layout, unsigned char *buf)
+{
+ const struct romentry *entry = NULL;
+
+ while ((entry = layout_next_included(layout, entry))) {
+ if (!entry->file)
+ continue;
+ const struct flash_region *region = &entry->region;
+ if (write_buf_to_file(buf + region->start,
+ region->end - region->start + 1, entry->file))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int do_read(struct flashctx *const flash, const char *const filename)
+{
+ int ret;
+
+ unsigned long size = flashrom_flash_getsize(flash);
+ unsigned char *buf = calloc(size, sizeof(unsigned char));
+ if (!buf) {
+ msg_gerr("Memory allocation failed!\n");
+ return 1;
+ }
+
+ ret = flashrom_image_read(flash, buf, size);
+ if (ret > 0)
+ goto free_out;
+
+ if (write_buf_to_include_args(get_layout(flash), buf)) {
+ ret = 1;
+ goto free_out;
+ }
+ if (filename)
+ ret = write_buf_to_file(buf, size, filename);
+
+free_out:
+ free(buf);
+ return ret;
+}
+
+static int do_extract(struct flashctx *const flash)
+{
+ prepare_layout_for_extraction(flash);
+ return do_read(flash, NULL);
+}
+
+static int do_write(struct flashctx *const flash, const char *const filename, const char *const referencefile)
+{
+ const size_t flash_size = flashrom_flash_getsize(flash);
+ int ret = 1;
+
+ uint8_t *const newcontents = malloc(flash_size);
+ uint8_t *const refcontents = referencefile ? malloc(flash_size) : NULL;
+
+ if (!newcontents || (referencefile && !refcontents)) {
+ msg_gerr("Out of memory!\n");
+ goto _free_ret;
+ }
+
+ /* Read '-w' argument first... */
+ 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.
+ */
+ if (read_buf_from_include_args(get_layout(flash), newcontents))
+ goto _free_ret;
+
+ if (referencefile) {
+ if (read_buf_from_file(refcontents, flash_size, referencefile))
+ goto _free_ret;
+ }
+
+ ret = flashrom_image_write(flash, newcontents, flash_size, refcontents);
+
+_free_ret:
+ free(refcontents);
+ free(newcontents);
+ return ret;
+}
+
+static int do_verify(struct flashctx *const flash, const char *const filename)
+{
+ const size_t flash_size = flashrom_flash_getsize(flash);
+ int ret = 1;
+
+ uint8_t *const newcontents = malloc(flash_size);
+ if (!newcontents) {
+ msg_gerr("Out of memory!\n");
+ goto _free_ret;
+ }
+
+ /* Read '-v' argument first... */
+ 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.
+ */
+ if (read_buf_from_include_args(get_layout(flash), newcontents))
+ goto _free_ret;
+
+ ret = flashrom_image_verify(flash, newcontents, flash_size);
+
+_free_ret:
+ free(newcontents);
+ return ret;
+}
+
+/* Returns the number of buses commonly supported by the current programmer and flash chip where the latter
+ * can not be completely accessed due to size/address limits of the programmer. */
+static unsigned int count_max_decode_exceedings(const struct flashctx *flash,
+ const struct decode_sizes *max_rom_decode_)
+{
+ unsigned int limitexceeded = 0;
+ uint32_t size = flash->chip->total_size * 1024;
+ enum chipbustype buses = flash->mst->buses_supported & flash->chip->bustype;
+
+ if ((buses & BUS_PARALLEL) && (max_rom_decode_->parallel < size)) {
+ limitexceeded++;
+ msg_pdbg("Chip size %"PRIu32" kB is bigger than supported "
+ "size %"PRIu32" kB of chipset/board/programmer "
+ "for %s interface, "
+ "probe/read/erase/write may fail. ", size / 1024,
+ max_rom_decode_->parallel / 1024, "Parallel");
+ }
+ if ((buses & BUS_LPC) && (max_rom_decode_->lpc < size)) {
+ limitexceeded++;
+ msg_pdbg("Chip size %"PRIu32" kB is bigger than supported "
+ "size %"PRIu32" kB of chipset/board/programmer "
+ "for %s interface, "
+ "probe/read/erase/write may fail. ", size / 1024,
+ max_rom_decode_->lpc / 1024, "LPC");
+ }
+ if ((buses & BUS_FWH) && (max_rom_decode_->fwh < size)) {
+ limitexceeded++;
+ msg_pdbg("Chip size %"PRIu32" kB is bigger than supported "
+ "size %"PRIu32" kB of chipset/board/programmer "
+ "for %s interface, "
+ "probe/read/erase/write may fail. ", size / 1024,
+ max_rom_decode_->fwh / 1024, "FWH");
+ }
+ if ((buses & BUS_SPI) && (max_rom_decode_->spi < size)) {
+ limitexceeded++;
+ msg_pdbg("Chip size %"PRIu32" kB is bigger than supported "
+ "size %"PRIu32" kB of chipset/board/programmer "
+ "for %s interface, "
+ "probe/read/erase/write may fail. ", size / 1024,
+ max_rom_decode_->spi / 1024, "SPI");
+ }
+ return limitexceeded;
+}
+
+static void parse_options(int argc, char **argv, const char *optstring,
+ const struct option *long_options,
+ struct cli_options *options)
+{
+ const char *name;
+ int namelen, opt;
+ int option_index = 0, operation_specified = 0;
- setbuf(stdout, NULL);
/* FIXME: Delay all operation_specified checks until after command
* line parsing to allow --help overriding everything else.
*/
@@ -236,34 +628,38 @@ int main(int argc, char *argv[])
switch (opt) {
case 'r':
cli_classic_validate_singleop(&operation_specified);
- filename = strdup(optarg);
- read_it = 1;
+ options->filename = strdup(optarg);
+ options->read_it = true;
break;
case 'w':
cli_classic_validate_singleop(&operation_specified);
- filename = strdup(optarg);
- write_it = 1;
+ options->filename = strdup(optarg);
+ options->write_it = true;
break;
case 'v':
//FIXME: gracefully handle superfluous -v
cli_classic_validate_singleop(&operation_specified);
- if (dont_verify_it) {
+ if (options->dont_verify_it) {
cli_classic_abort_usage("--verify and --noverify are mutually exclusive. Aborting.\n");
}
- filename = strdup(optarg);
- verify_it = 1;
+ options->filename = strdup(optarg);
+ options->verify_it = true;
break;
case 'n':
- if (verify_it) {
+ if (options->verify_it) {
cli_classic_abort_usage("--verify and --noverify are mutually exclusive. Aborting.\n");
}
- dont_verify_it = 1;
+ options->dont_verify_it = true;
break;
case 'N':
- dont_verify_all = 1;
+ options->dont_verify_all = true;
+ break;
+ case 'x':
+ cli_classic_validate_singleop(&operation_specified);
+ options->extract_it = true;
break;
case 'c':
- chip_to_probe = strdup(optarg);
+ options->chip_to_probe = strdup(optarg);
break;
case 'V':
verbose_screen++;
@@ -272,123 +668,125 @@ int main(int argc, char *argv[])
break;
case 'E':
cli_classic_validate_singleop(&operation_specified);
- erase_it = 1;
+ options->erase_it = true;
break;
case 'f':
- force = 1;
+ options->force = true;
break;
case 'l':
- if (layoutfile)
+ if (options->layoutfile)
cli_classic_abort_usage("Error: --layout specified more than once. Aborting.\n");
- if (ifd)
+ if (options->ifd)
cli_classic_abort_usage("Error: --layout and --ifd both specified. Aborting.\n");
- if (fmap)
+ if (options->fmap)
cli_classic_abort_usage("Error: --layout and --fmap-file both specified. Aborting.\n");
- layoutfile = strdup(optarg);
+ options->layoutfile = strdup(optarg);
break;
case OPTION_IFD:
- if (layoutfile)
+ if (options->layoutfile)
cli_classic_abort_usage("Error: --layout and --ifd both specified. Aborting.\n");
- if (fmap)
+ if (options->fmap)
cli_classic_abort_usage("Error: --fmap-file and --ifd both specified. Aborting.\n");
- ifd = 1;
+ options->ifd = true;
break;
case OPTION_FMAP_FILE:
- if (fmap)
+ if (options->fmap)
cli_classic_abort_usage("Error: --fmap or --fmap-file specified "
"more than once. Aborting.\n");
- if (ifd)
+ if (options->ifd)
cli_classic_abort_usage("Error: --fmap-file and --ifd both specified. Aborting.\n");
- if (layoutfile)
+ if (options->layoutfile)
cli_classic_abort_usage("Error: --fmap-file and --layout both specified. Aborting.\n");
- fmapfile = strdup(optarg);
- fmap = 1;
+ options->fmapfile = strdup(optarg);
+ options->fmap = true;
break;
case OPTION_FMAP:
- if (fmap)
+ if (options->fmap)
cli_classic_abort_usage("Error: --fmap or --fmap-file specified "
"more than once. Aborting.\n");
- if (ifd)
+ if (options->ifd)
cli_classic_abort_usage("Error: --fmap and --ifd both specified. Aborting.\n");
- if (layoutfile)
+ if (options->layoutfile)
cli_classic_abort_usage("Error: --layout and --fmap both specified. Aborting.\n");
- fmap = 1;
+ options->fmap = true;
break;
case 'i':
- tempstr = strdup(optarg);
- if (register_include_arg(&include_args, tempstr)) {
- free(tempstr);
+ if (register_include_arg(&options->include_args, optarg))
cli_classic_abort_usage(NULL);
- }
break;
case OPTION_FLASH_CONTENTS:
- if (referencefile)
+ if (options->referencefile)
cli_classic_abort_usage("Error: --flash-contents specified more than once."
"Aborting.\n");
- referencefile = strdup(optarg);
+ options->referencefile = strdup(optarg);
break;
case OPTION_FLASH_NAME:
cli_classic_validate_singleop(&operation_specified);
- flash_name = 1;
+ options->flash_name = true;
break;
case OPTION_FLASH_SIZE:
cli_classic_validate_singleop(&operation_specified);
- flash_size = 1;
+ options->flash_size = true;
break;
case OPTION_WP_STATUS:
- wp_status = 1;
+ options->print_wp_status = true;
break;
case OPTION_WP_LIST:
- wp_list = 1;
+ options->print_wp_ranges = true;
break;
case OPTION_WP_SET_RANGE:
- if (parse_wp_range(&wp_start, &wp_len) < 0)
+ if (parse_wp_range(&options->wp_start, &options->wp_len) < 0)
cli_classic_abort_usage("Incorrect wp-range arguments provided.\n");
- set_wp_range = 1;
+ options->set_wp_range = true;
+ break;
+ case OPTION_WP_SET_REGION:
+ options->set_wp_region = true;
+ options->wp_region = strdup(optarg);
break;
case OPTION_WP_ENABLE:
- set_wp_enable = 1;
- if (optarg)
- wp_mode_opt = strdup(optarg);
+ options->enable_wp = true;
break;
case OPTION_WP_DISABLE:
- set_wp_disable = 1;
+ options->disable_wp = true;
break;
case 'L':
cli_classic_validate_singleop(&operation_specified);
- list_supported = 1;
+ options->list_supported = true;
break;
case 'z':
#if CONFIG_PRINT_WIKI == 1
cli_classic_validate_singleop(&operation_specified);
- list_supported_wiki = 1;
+ options->list_supported_wiki = true;
#else
cli_classic_abort_usage("Error: Wiki output was not "
"compiled in. Aborting.\n");
#endif
break;
case 'p':
- if (prog != PROGRAMMER_INVALID) {
+ if (options->prog != NULL) {
cli_classic_abort_usage("Error: --programmer specified "
"more than once. You can separate "
"multiple\nparameters for a programmer "
"with \",\". Please see the man page "
"for details.\n");
}
- for (prog = 0; prog < PROGRAMMER_INVALID; prog++) {
- name = programmer_table[prog].name;
+ size_t p;
+ for (p = 0; p < programmer_table_size; p++) {
+ name = programmer_table[p]->name;
namelen = strlen(name);
if (strncmp(optarg, name, namelen) == 0) {
switch (optarg[namelen]) {
case ':':
- pparam = strdup(optarg + namelen + 1);
- if (!strlen(pparam)) {
- free(pparam);
- pparam = NULL;
+ options->pparam = strdup(optarg + namelen + 1);
+ if (!strlen(options->pparam)) {
+ free(options->pparam);
+ options->pparam = NULL;
}
+ options->prog = programmer_table[p];
break;
case '\0':
+ options->prog = programmer_table[p];
break;
default:
/* The continue refers to the
@@ -401,7 +799,7 @@ int main(int argc, char *argv[])
break;
}
}
- if (prog == PROGRAMMER_INVALID) {
+ if (options->prog == NULL) {
fprintf(stderr, "Error: Unknown programmer \"%s\". Valid choices are:\n",
optarg);
list_programmers_linebreak(0, 80, 0);
@@ -420,19 +818,18 @@ int main(int argc, char *argv[])
exit(0);
break;
case 'o':
-#ifdef STANDALONE
- cli_classic_abort_usage("Log file not supported in standalone mode. Aborting.\n");
-#else /* STANDALONE */
- if (logfile) {
+ if (options->logfile) {
fprintf(stderr, "Warning: -o/--output specified multiple times.\n");
- free(logfile);
+ free(options->logfile);
}
- logfile = strdup(optarg);
- if (logfile[0] == '\0') {
+ options->logfile = strdup(optarg);
+ if (options->logfile[0] == '\0') {
cli_classic_abort_usage("No log filename specified.\n");
}
-#endif /* STANDALONE */
+ break;
+ case OPTION_PROGRESS:
+ options->show_progress = true;
break;
default:
cli_classic_abort_usage(NULL);
@@ -442,38 +839,119 @@ int main(int argc, char *argv[])
if (optind < argc)
cli_classic_abort_usage("Error: Extra parameter found.\n");
- if ((read_it | write_it | verify_it) && check_filename(filename, "image"))
+}
+
+static void free_options(struct cli_options *options)
+{
+ cleanup_include_args(&options->include_args);
+ free(options->filename);
+ free(options->fmapfile);
+ free(options->referencefile);
+ free(options->layoutfile);
+ free(options->pparam);
+ free(options->wp_region);
+ free(options->logfile);
+ free((char *)options->chip_to_probe);
+}
+
+int main(int argc, char *argv[])
+{
+ const struct flashchip *chip = NULL;
+ /* Probe for up to eight flash chips. */
+ struct flashctx flashes[8] = {{0}};
+ struct flashctx *fill_flash;
+ char *tempstr = NULL;
+ int startchip = -1, chipcount = 0;
+ int i, j;
+ int ret = 0;
+
+ struct cli_options options = { 0 };
+ 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'},
+ {"erase", 0, NULL, 'E'},
+ {"verify", 1, NULL, 'v'},
+ {"noverify", 0, NULL, 'n'},
+ {"noverify-all", 0, NULL, 'N'},
+ {"extract", 0, NULL, 'x'},
+ {"chip", 1, NULL, 'c'},
+ {"verbose", 0, NULL, 'V'},
+ {"force", 0, NULL, 'f'},
+ {"layout", 1, NULL, 'l'},
+ {"ifd", 0, NULL, OPTION_IFD},
+ {"fmap", 0, NULL, OPTION_FMAP},
+ {"fmap-file", 1, NULL, OPTION_FMAP_FILE},
+ {"image", 1, NULL, 'i'}, // (deprecated): back compatibility.
+ {"include", 1, NULL, 'i'},
+ {"flash-contents", 1, NULL, OPTION_FLASH_CONTENTS},
+ {"flash-name", 0, NULL, OPTION_FLASH_NAME},
+ {"flash-size", 0, NULL, OPTION_FLASH_SIZE},
+ {"get-size", 0, NULL, OPTION_FLASH_SIZE}, // (deprecated): back compatibility.
+ {"wp-status", 0, NULL, OPTION_WP_STATUS},
+ {"wp-list", 0, NULL, OPTION_WP_LIST},
+ {"wp-range", 1, NULL, OPTION_WP_SET_RANGE},
+ {"wp-region", 1, NULL, OPTION_WP_SET_REGION},
+ {"wp-enable", 0, NULL, OPTION_WP_ENABLE},
+ {"wp-disable", 0, NULL, OPTION_WP_DISABLE},
+ {"list-supported", 0, NULL, 'L'},
+ {"list-supported-wiki", 0, NULL, 'z'},
+ {"programmer", 1, NULL, 'p'},
+ {"help", 0, NULL, 'h'},
+ {"version", 0, NULL, 'R'},
+ {"output", 1, NULL, 'o'},
+ {"progress", 0, NULL, OPTION_PROGRESS},
+ {NULL, 0, NULL, 0},
+ };
+
+ /*
+ * Safety-guard against a user who has (mistakenly) closed
+ * stdout or stderr before exec'ing flashrom. We disable
+ * logging in this case to prevent writing log data to a flash
+ * chip when a flash device gets opened with fd 1 or 2.
+ */
+ if (check_file(stdout) && check_file(stderr)) {
+ flashrom_set_log_callback(&flashrom_print_cb);
+ }
+
+ print_version();
+ print_banner();
+
+ /* FIXME: Delay calibration should happen in programmer code. */
+ if (flashrom_init(1))
+ exit(1);
+
+ setbuf(stdout, NULL);
+
+ parse_options(argc, argv, optstring, long_options, &options);
+
+ if ((options.read_it | options.write_it | options.verify_it) && check_filename(options.filename, "image"))
cli_classic_abort_usage(NULL);
- if (layoutfile && check_filename(layoutfile, "layout"))
+ if (options.layoutfile && check_filename(options.layoutfile, "layout"))
cli_classic_abort_usage(NULL);
- if (fmapfile && check_filename(fmapfile, "fmap"))
+ if (options.fmapfile && check_filename(options.fmapfile, "fmap"))
cli_classic_abort_usage(NULL);
- if (referencefile && check_filename(referencefile, "reference"))
+ if (options.referencefile && check_filename(options.referencefile, "reference"))
cli_classic_abort_usage(NULL);
-
-#ifndef STANDALONE
- if (logfile && check_filename(logfile, "log"))
+ if (options.logfile && check_filename(options.logfile, "log"))
cli_classic_abort_usage(NULL);
- if (logfile && open_logfile(logfile))
+ if (options.logfile && open_logfile(options.logfile))
cli_classic_abort_usage(NULL);
-#endif /* !STANDALONE */
#if CONFIG_PRINT_WIKI == 1
- if (list_supported_wiki) {
+ if (options.list_supported_wiki) {
print_supported_wiki();
goto out;
}
#endif
- if (list_supported) {
+ if (options.list_supported) {
if (print_supported())
ret = 1;
goto out;
}
-#ifndef STANDALONE
start_logging();
-#endif /* !STANDALONE */
print_buildinfo();
msg_gdbg("Command line (%i args):", argc - 1);
@@ -482,22 +960,22 @@ int main(int argc, char *argv[])
}
msg_gdbg("\n");
- if (layoutfile && read_romlayout(layoutfile)) {
+ if (options.layoutfile && layout_from_file(&options.layout, options.layoutfile)) {
ret = 1;
goto out;
}
- if (!ifd && !fmap && process_include_args(get_global_layout(), include_args)) {
+ if (!options.ifd && !options.fmap && process_include_args(options.layout, options.include_args)) {
ret = 1;
goto out;
}
/* Does a chip with the requested name exist in the flashchips array? */
- if (chip_to_probe) {
+ if (options.chip_to_probe) {
for (chip = flashchips; chip && chip->name; chip++)
- if (!strcmp(chip->name, chip_to_probe))
+ if (!strcmp(chip->name, options.chip_to_probe))
break;
if (!chip || !chip->name) {
- msg_cerr("Error: Unknown chip '%s' specified.\n", chip_to_probe);
+ msg_cerr("Error: Unknown chip '%s' specified.\n", options.chip_to_probe);
msg_gerr("Run flashrom -L to view the hardware supported in this flashrom version.\n");
ret = 1;
goto out;
@@ -505,13 +983,15 @@ int main(int argc, char *argv[])
/* Keep chip around for later usage in case a forced read is requested. */
}
- if (prog == PROGRAMMER_INVALID) {
- if (CONFIG_DEFAULT_PROGRAMMER != PROGRAMMER_INVALID) {
- prog = CONFIG_DEFAULT_PROGRAMMER;
+ if (options.prog == NULL) {
+ const struct programmer_entry *const default_programmer = CONFIG_DEFAULT_PROGRAMMER_NAME;
+
+ if (default_programmer) {
+ options.prog = default_programmer;
/* We need to strdup here because we free(pparam) unconditionally later. */
- pparam = strdup(CONFIG_DEFAULT_PROGRAMMER_ARGS);
+ options.pparam = strdup(CONFIG_DEFAULT_PROGRAMMER_ARGS);
msg_pinfo("Using default programmer \"%s\" with arguments \"%s\".\n",
- programmer_table[CONFIG_DEFAULT_PROGRAMMER].name, pparam);
+ default_programmer->name, options.pparam);
} else {
msg_perr("Please select a programmer with the --programmer parameter.\n"
#if CONFIG_INTERNAL == 1
@@ -525,22 +1005,19 @@ int main(int argc, char *argv[])
}
}
- /* FIXME: Delay calibration should happen in programmer code. */
- myusec_calibrate_delay();
-
- if (programmer_init(prog, pparam)) {
+ if (programmer_init(options.prog, options.pparam)) {
msg_perr("Error: Programmer initialization failed.\n");
ret = 1;
goto out_shutdown;
}
tempstr = flashbuses_to_text(get_buses_supported());
- msg_pdbg("The following protocols are supported: %s.\n", tempstr);
+ msg_pdbg("The following protocols are supported: %s.\n", tempstr ? tempstr : "?");
free(tempstr);
for (j = 0; j < registered_master_count; j++) {
startchip = 0;
while (chipcount < (int)ARRAY_SIZE(flashes)) {
- startchip = probe_flash(&registered_masters[j], startchip, &flashes[chipcount], 0);
+ startchip = probe_flash(&registered_masters[j], startchip, &flashes[chipcount], 0, options.chip_to_probe);
if (startchip == -1)
break;
chipcount++;
@@ -558,11 +1035,11 @@ int main(int argc, char *argv[])
goto out_shutdown;
} else if (!chipcount) {
msg_cinfo("No EEPROM/flash device found.\n");
- if (!force || !chip_to_probe) {
+ if (!options.force || !options.chip_to_probe) {
msg_cinfo("Note: flashrom can never write if the flash chip isn't found "
"automatically.\n");
}
- if (force && read_it && chip_to_probe) {
+ if (options.force && options.read_it && options.chip_to_probe) {
struct registered_master *mst;
int compatible_masters = 0;
msg_cinfo("Force read (-f -r -c) requested, pretending the chip is there:\n");
@@ -583,43 +1060,46 @@ int main(int argc, char *argv[])
"chip, using the first one.\n");
for (j = 0; j < registered_master_count; j++) {
mst = &registered_masters[j];
- startchip = probe_flash(mst, 0, &flashes[0], 1);
+ startchip = probe_flash(mst, 0, &flashes[0], 1, options.chip_to_probe);
if (startchip != -1)
break;
}
if (startchip == -1) {
// FIXME: This should never happen! Ask for a bug report?
- msg_cinfo("Probing for flash chip '%s' failed.\n", chip_to_probe);
- ret = 1;
- goto out_shutdown;
- }
- if (map_flash(&flashes[0]) != 0) {
- free(flashes[0].chip);
+ msg_cinfo("Probing for flash chip '%s' failed.\n", options.chip_to_probe);
ret = 1;
goto out_shutdown;
}
msg_cinfo("Please note that forced reads most likely contain garbage.\n");
- ret = read_flash_to_file(&flashes[0], filename);
- unmap_flash(&flashes[0]);
+ flashrom_flag_set(&flashes[0], FLASHROM_FLAG_FORCE, options.force);
+ ret = do_read(&flashes[0], options.filename);
free(flashes[0].chip);
goto out_shutdown;
}
ret = 1;
goto out_shutdown;
- } else if (!chip_to_probe) {
+ } else if (!options.chip_to_probe) {
/* repeat for convenience when looking at foreign logs */
tempstr = flashbuses_to_text(flashes[0].chip->bustype);
msg_gdbg("Found %s flash chip \"%s\" (%d kB, %s).\n",
- flashes[0].chip->vendor, flashes[0].chip->name, flashes[0].chip->total_size, tempstr);
+ flashes[0].chip->vendor, flashes[0].chip->name, flashes[0].chip->total_size,
+ tempstr ? tempstr : "?");
free(tempstr);
}
fill_flash = &flashes[0];
+ unsigned int progress_user_data[FLASHROM_PROGRESS_NR];
+ struct flashrom_progress progress_state = {
+ .user_data = progress_user_data
+ };
+ if (options.show_progress)
+ flashrom_set_progress_callback(fill_flash, &flashrom_progress_cb, &progress_state);
+
print_chip_support_status(fill_flash->chip);
- unsigned int limitexceeded = count_max_decode_exceedings(fill_flash);
- if (limitexceeded > 0 && !force) {
+ unsigned int limitexceeded = count_max_decode_exceedings(fill_flash, &max_rom_decode);
+ if (limitexceeded > 0 && !options.force) {
enum chipbustype commonbuses = fill_flash->mst->buses_supported & fill_flash->chip->bustype;
/* Sometimes chip and programmer have more than one bus in common,
@@ -634,33 +1114,31 @@ int main(int argc, char *argv[])
goto out_shutdown;
}
- if (!(read_it | write_it | verify_it | erase_it | flash_name | flash_size
- | set_wp_range | set_wp_region | set_wp_enable |
- set_wp_disable | wp_status | wp_list)) {
+ const bool any_wp_op =
+ options.set_wp_range || options.set_wp_region || options.enable_wp ||
+ options.disable_wp || options.print_wp_status || options.print_wp_ranges;
+
+ const bool any_op = options.read_it || options.write_it || options.verify_it ||
+ options.erase_it || options.flash_name || options.flash_size ||
+ options.extract_it || any_wp_op;
+
+ if (!any_op) {
msg_ginfo("No operations were specified.\n");
goto out_shutdown;
}
- if (set_wp_enable && set_wp_disable) {
+ if (options.enable_wp && options.disable_wp) {
msg_ginfo("Error: --wp-enable and --wp-disable are mutually exclusive\n");
ret = 1;
goto out_shutdown;
}
- if (set_wp_range && set_wp_region) {
+ if (options.set_wp_range && options.set_wp_region) {
msg_gerr("Error: Cannot use both --wp-range and --wp-region simultaneously.\n");
ret = 1;
goto out_shutdown;
}
- if (set_wp_range || set_wp_region) {
- if (!fill_flash->chip->wp || !fill_flash->chip->wp->set_range) {
- msg_gerr("Error: write protect is not supported on this flash chip.\n");
- ret = 1;
- goto out_shutdown;
- }
- }
-
- if (flash_name) {
+ if (options.flash_name) {
if (fill_flash->chip->vendor && fill_flash->chip->name) {
printf("vendor=\"%s\" name=\"%s\"\n",
fill_flash->chip->vendor,
@@ -671,81 +1149,19 @@ int main(int argc, char *argv[])
goto out_shutdown;
}
- if (flash_size) {
- printf("%d\n", fill_flash->chip->total_size * 1024);
- goto out_shutdown;
- }
-
- if (wp_status) {
- if (fill_flash->chip->wp && fill_flash->chip->wp->wp_status) {
- ret |= fill_flash->chip->wp->wp_status(fill_flash);
- } else {
- msg_gerr("Error: write protect is not supported on this flash chip.\n");
- ret = 1;
- }
- goto out_shutdown;
- }
-
- /* Note: set_wp_disable should be done before setting the range */
- if (set_wp_disable) {
- if (fill_flash->chip->wp && fill_flash->chip->wp->disable) {
- ret |= fill_flash->chip->wp->disable(fill_flash);
- } else {
- msg_gerr("Error: write protect is not supported on this flash chip.\n");
- ret = 1;
- goto out_shutdown;
- }
- }
-
- if (!ret && set_wp_enable) {
- enum wp_mode wp_mode;
-
- if (wp_mode_opt)
- wp_mode = get_wp_mode(wp_mode_opt);
- else
- wp_mode = WP_MODE_HARDWARE; /* default */
-
- if (wp_mode == WP_MODE_UNKNOWN) {
- msg_gerr("Error: Invalid WP mode: \"%s\"\n", wp_mode_opt);
- ret = 1;
- goto out_shutdown;
- }
-
- if (fill_flash->chip->wp && fill_flash->chip->wp->enable) {
- ret |= fill_flash->chip->wp->enable(fill_flash, wp_mode);
- } else {
- msg_gerr("Error: write protect is not supported on this flash chip.\n");
- ret = 1;
- goto out_shutdown;
- }
- }
-
- if (wp_list) {
- msg_ginfo("Valid write protection ranges:\n");
- if (fill_flash->chip->wp && fill_flash->chip->wp->list_ranges) {
- ret |= fill_flash->chip->wp->list_ranges(fill_flash);
- } else {
- msg_gerr("Error: write protect is not supported on this flash chip.\n");
- ret = 1;
- }
+ if (options.flash_size) {
+ printf("%zu\n", flashrom_flash_getsize(fill_flash));
goto out_shutdown;
}
- /* Note: set_wp_range must happen before set_wp_enable */
- if (set_wp_range) {
- ret |= fill_flash->chip->wp->set_range(fill_flash, wp_start, wp_len);
- }
-
- if (layoutfile) {
- layout = get_global_layout();
- } else if (ifd && (flashrom_layout_read_from_ifd(&layout, fill_flash, NULL, 0) ||
- process_include_args(layout, include_args))) {
+ if (options.ifd && (flashrom_layout_read_from_ifd(&options.layout, fill_flash, NULL, 0) ||
+ process_include_args(options.layout, options.include_args))) {
ret = 1;
goto out_shutdown;
- } else if (fmap && fmapfile) {
+ } else if (options.fmap && options.fmapfile) {
struct stat s;
- if (stat(fmapfile, &s) != 0) {
- msg_gerr("Failed to stat fmapfile \"%s\"\n", fmapfile);
+ if (stat(options.fmapfile, &s) != 0) {
+ msg_gerr("Failed to stat fmapfile \"%s\"\n", options.fmapfile);
ret = 1;
goto out_shutdown;
}
@@ -757,67 +1173,100 @@ int main(int argc, char *argv[])
goto out_shutdown;
}
- if (read_buf_from_file(fmapfile_buffer, fmapfile_size, fmapfile)) {
+ if (read_buf_from_file(fmapfile_buffer, fmapfile_size, options.fmapfile)) {
ret = 1;
free(fmapfile_buffer);
goto out_shutdown;
}
- if (flashrom_layout_read_fmap_from_buffer(&layout, fill_flash, fmapfile_buffer, fmapfile_size) ||
- process_include_args(layout, include_args)) {
+ if (flashrom_layout_read_fmap_from_buffer(&options.layout, fill_flash, fmapfile_buffer, fmapfile_size) ||
+ process_include_args(options.layout, options.include_args)) {
ret = 1;
free(fmapfile_buffer);
goto out_shutdown;
}
free(fmapfile_buffer);
- } else if (fmap && (flashrom_layout_read_fmap_from_rom(&layout, fill_flash, 0,
- fill_flash->chip->total_size * 1024) || process_include_args(layout, include_args))) {
+ } else if (options.fmap && (flashrom_layout_read_fmap_from_rom(&options.layout, fill_flash, 0,
+ flashrom_flash_getsize(fill_flash)) ||
+ process_include_args(options.layout, options.include_args))) {
ret = 1;
goto out_shutdown;
}
+ flashrom_layout_set(fill_flash, options.layout);
- flashrom_layout_set(fill_flash, layout);
- flashrom_flag_set(fill_flash, FLASHROM_FLAG_FORCE, !!force);
+ if (any_wp_op) {
+ if (options.set_wp_region && options.wp_region) {
+ if (!options.layout) {
+ msg_gerr("Error: A flash layout must be specified to use --wp-region.\n");
+ ret = 1;
+ goto out_release;
+ }
+
+ ret = flashrom_layout_get_region_range(options.layout, options.wp_region, &options.wp_start, &options.wp_len);
+ if (ret) {
+ msg_gerr("Error: Region %s not found in flash layout.\n", options.wp_region);
+ goto out_release;
+ }
+ options.set_wp_range = true;
+ }
+ ret = wp_cli(
+ fill_flash,
+ options.enable_wp,
+ options.disable_wp,
+ options.print_wp_status,
+ options.print_wp_ranges,
+ options.set_wp_range,
+ options.wp_start,
+ options.wp_len
+ );
+ if (ret)
+ goto out_release;
+ }
+
+ flashrom_flag_set(fill_flash, FLASHROM_FLAG_FORCE, options.force);
#if CONFIG_INTERNAL == 1
- flashrom_flag_set(fill_flash, FLASHROM_FLAG_FORCE_BOARDMISMATCH, !!force_boardmismatch);
+ flashrom_flag_set(fill_flash, FLASHROM_FLAG_FORCE_BOARDMISMATCH, force_boardmismatch);
#endif
- flashrom_flag_set(fill_flash, FLASHROM_FLAG_VERIFY_AFTER_WRITE, !dont_verify_it);
- flashrom_flag_set(fill_flash, FLASHROM_FLAG_VERIFY_WHOLE_CHIP, !dont_verify_all);
+ flashrom_flag_set(fill_flash, FLASHROM_FLAG_VERIFY_AFTER_WRITE, !options.dont_verify_it);
+ flashrom_flag_set(fill_flash, FLASHROM_FLAG_VERIFY_WHOLE_CHIP, !options.dont_verify_all);
/* FIXME: We should issue an unconditional chip reset here. This can be
* done once we have a .reset function in struct flashchip.
* Give the chip time to settle.
*/
- programmer_delay(100000);
- if (read_it)
- ret = do_read(fill_flash, filename);
- else if (erase_it)
- ret = do_erase(fill_flash);
- else if (write_it)
- ret = do_write(fill_flash, filename, referencefile);
- else if (verify_it)
- ret = do_verify(fill_flash, filename);
-
- flashrom_layout_release(layout);
+ programmer_delay(fill_flash, 100000);
+ if (options.read_it)
+ ret = do_read(fill_flash, options.filename);
+ else if (options.extract_it)
+ ret = do_extract(fill_flash);
+ else if (options.erase_it) {
+ ret = flashrom_flash_erase(fill_flash);
+ /*
+ * FIXME: Do we really want the scary warning if erase failed?
+ * After all, after erase the chip is either blank or partially
+ * blank or it has the old contents. A blank chip won't boot,
+ * so if the user wanted erase and reboots afterwards, the user
+ * knows very well that booting won't work.
+ */
+ if (ret)
+ emergency_help_message();
+ }
+ else if (options.write_it)
+ ret = do_write(fill_flash, options.filename, options.referencefile);
+ else if (options.verify_it)
+ ret = do_verify(fill_flash, options.filename);
+out_release:
+ flashrom_layout_release(options.layout);
out_shutdown:
- programmer_shutdown();
+ flashrom_programmer_shutdown(NULL);
out:
- for (i = 0; i < chipcount; i++)
+ for (i = 0; i < chipcount; i++) {
+ flashrom_layout_release(flashes[i].default_layout);
free(flashes[i].chip);
+ }
- layout_cleanup(&include_args);
- free(filename);
- free(fmapfile);
- free(referencefile);
- free(layoutfile);
- free(pparam);
- /* clean up global variables */
- free((char *)chip_to_probe); /* Silence! Freeing is not modifying contents. */
- chip_to_probe = NULL;
-#ifndef STANDALONE
- free(logfile);
+ free_options(&options);
ret |= close_logfile();
-#endif /* !STANDALONE */
return ret;
}