mirror of
https://review.coreboot.org/flashrom.git
synced 2025-04-27 07:02:34 +02:00

Some flash chips need time to exit ID mode, and while we take care of correct timing for the matching probe, subsequent probes may have totally different timing, and that can lead to garbage responses from the flash chip during the first accesses after the probe sequence is done. Delay 100 ms between the last probe and any subsequent operation. To ensure maximum correctness, we would have to reset the chip first in case the last probe function left the chip in an undefined (non-read) state. That will be possible once struct flashchip has a .reset function. This fixes unstable erase/read/write for some flahs chips on nic3com and possible other use cases as well. Thanks to Maciej Pijanka for reporting the issue and testing patches. Corresponding to flashrom svn r1172. Signed-off-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net> Acked-by: Michael Karcher <flashrom@mkarcher.dialup.fu-berlin.de>
466 lines
13 KiB
C
466 lines
13 KiB
C
/*
|
|
* This file is part of the flashrom project.
|
|
*
|
|
* Copyright (C) 2000 Silicon Integrated System Corporation
|
|
* Copyright (C) 2004 Tyan Corp <yhlu@tyan.com>
|
|
* Copyright (C) 2005-2008 coresystems GmbH
|
|
* Copyright (C) 2008,2009,2010 Carl-Daniel Hailfinger
|
|
*
|
|
* 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.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <getopt.h>
|
|
#include "flash.h"
|
|
#include "flashchips.h"
|
|
#include "programmer.h"
|
|
|
|
static void cli_classic_usage(const char *name)
|
|
{
|
|
const char *pname;
|
|
int pnamelen;
|
|
int remaining = 0;
|
|
enum programmer p;
|
|
|
|
printf("Usage: flashrom [-n] [-V] [-f] [-h|-R|-L|"
|
|
#if CONFIG_PRINT_WIKI == 1
|
|
"-z|"
|
|
#endif
|
|
"-E|-r <file>|-w <file>|-v <file>]\n"
|
|
" [-c <chipname>] [-m [<vendor>:]<part>] [-l <file>]\n"
|
|
" [-i <image>] [-p <programmername>[:<parameters>]]\n\n");
|
|
|
|
printf("Please note that the command line interface for flashrom has "
|
|
"changed between\n"
|
|
"0.9.1 and 0.9.2 and will change again before flashrom 1.0.\n"
|
|
"Do not use flashrom in scripts or other automated tools "
|
|
"without checking\n"
|
|
"that your flashrom version won't interpret options in a "
|
|
"different way.\n\n");
|
|
|
|
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"
|
|
" -E | --erase erase flash device\n"
|
|
" -V | --verbose more verbose output\n"
|
|
" -c | --chip <chipname> probe only for specified "
|
|
"flash chip\n"
|
|
#if CONFIG_INTERNAL == 1
|
|
/* FIXME: --mainboard should be a programmer parameter */
|
|
" -m | --mainboard <[vendor:]part> override mainboard "
|
|
"detection\n"
|
|
#endif
|
|
" -f | --force force specific operations "
|
|
"(see man page)\n"
|
|
" -n | --noverify don't auto-verify\n"
|
|
" -l | --layout <file> read ROM layout from "
|
|
"<file>\n"
|
|
" -i | --image <name> only flash image <name> "
|
|
"from flash layout\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
|
|
" -p | --programmer <name>[:<param>] specify the programmer "
|
|
"device");
|
|
|
|
for (p = 0; p < PROGRAMMER_INVALID; p++) {
|
|
pname = programmer_table[p].name;
|
|
pnamelen = strlen(pname);
|
|
if (remaining - pnamelen - 2 < 0) {
|
|
printf("\n ");
|
|
remaining = 43;
|
|
} else {
|
|
printf(" ");
|
|
remaining--;
|
|
}
|
|
if (p == 0) {
|
|
printf("(");
|
|
remaining--;
|
|
}
|
|
printf("%s", pname);
|
|
remaining -= pnamelen;
|
|
if (p < PROGRAMMER_INVALID - 1) {
|
|
printf(",");
|
|
remaining--;
|
|
} else {
|
|
printf(")\n");
|
|
}
|
|
}
|
|
|
|
printf("\nYou can specify one of -h, -R, -L, "
|
|
#if CONFIG_PRINT_WIKI == 1
|
|
"-z, "
|
|
#endif
|
|
"-E, -r, -w, -v or no operation.\n"
|
|
"If no operation is specified, flashrom will only probe for "
|
|
"flash chips.\n\n");
|
|
}
|
|
|
|
static void cli_classic_abort_usage(void)
|
|
{
|
|
printf("Please run \"flashrom --help\" for usage info.\n");
|
|
exit(1);
|
|
}
|
|
|
|
int cli_classic(int argc, char *argv[])
|
|
{
|
|
unsigned long size;
|
|
/* Probe for up to three flash chips. */
|
|
struct flashchip *flash, *flashes[3];
|
|
const char *name;
|
|
int namelen;
|
|
int opt;
|
|
int option_index = 0;
|
|
int force = 0;
|
|
int read_it = 0, write_it = 0, erase_it = 0, verify_it = 0;
|
|
int dont_verify_it = 0, list_supported = 0;
|
|
#if CONFIG_PRINT_WIKI == 1
|
|
int list_supported_wiki = 0;
|
|
#endif
|
|
int operation_specified = 0;
|
|
int i;
|
|
|
|
const char *optstring = "r:Rw:v:nVEfc:m:l:i:p:Lzh";
|
|
static struct option long_options[] = {
|
|
{"read", 1, 0, 'r'},
|
|
{"write", 1, 0, 'w'},
|
|
{"erase", 0, 0, 'E'},
|
|
{"verify", 1, 0, 'v'},
|
|
{"noverify", 0, 0, 'n'},
|
|
{"chip", 1, 0, 'c'},
|
|
{"mainboard", 1, 0, 'm'},
|
|
{"verbose", 0, 0, 'V'},
|
|
{"force", 0, 0, 'f'},
|
|
{"layout", 1, 0, 'l'},
|
|
{"image", 1, 0, 'i'},
|
|
{"list-supported", 0, 0, 'L'},
|
|
{"list-supported-wiki", 0, 0, 'z'},
|
|
{"programmer", 1, 0, 'p'},
|
|
{"help", 0, 0, 'h'},
|
|
{"version", 0, 0, 'R'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
char *filename = NULL;
|
|
|
|
char *tempstr = NULL;
|
|
char *pparam = NULL;
|
|
|
|
print_version();
|
|
print_banner();
|
|
|
|
if (selfcheck())
|
|
exit(1);
|
|
|
|
setbuf(stdout, NULL);
|
|
/* FIXME: Delay all operation_specified checks until after command
|
|
* line parsing to allow --help overriding everything else.
|
|
*/
|
|
while ((opt = getopt_long(argc, argv, optstring,
|
|
long_options, &option_index)) != EOF) {
|
|
switch (opt) {
|
|
case 'r':
|
|
if (++operation_specified > 1) {
|
|
fprintf(stderr, "More than one operation "
|
|
"specified. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
filename = strdup(optarg);
|
|
read_it = 1;
|
|
break;
|
|
case 'w':
|
|
if (++operation_specified > 1) {
|
|
fprintf(stderr, "More than one operation "
|
|
"specified. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
filename = strdup(optarg);
|
|
write_it = 1;
|
|
break;
|
|
case 'v':
|
|
//FIXME: gracefully handle superfluous -v
|
|
if (++operation_specified > 1) {
|
|
fprintf(stderr, "More than one operation "
|
|
"specified. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
if (dont_verify_it) {
|
|
fprintf(stderr, "--verify and --noverify are"
|
|
"mutually exclusive. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
filename = strdup(optarg);
|
|
verify_it = 1;
|
|
break;
|
|
case 'n':
|
|
if (verify_it) {
|
|
fprintf(stderr, "--verify and --noverify are"
|
|
"mutually exclusive. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
dont_verify_it = 1;
|
|
break;
|
|
case 'c':
|
|
chip_to_probe = strdup(optarg);
|
|
break;
|
|
case 'V':
|
|
verbose++;
|
|
break;
|
|
case 'E':
|
|
if (++operation_specified > 1) {
|
|
fprintf(stderr, "More than one operation "
|
|
"specified. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
erase_it = 1;
|
|
break;
|
|
case 'm':
|
|
#if CONFIG_INTERNAL == 1
|
|
tempstr = strdup(optarg);
|
|
lb_vendor_dev_from_string(tempstr);
|
|
#else
|
|
fprintf(stderr, "Error: Internal programmer support "
|
|
"was not compiled in and --mainboard only\n"
|
|
"applies to the internal programmer. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
#endif
|
|
break;
|
|
case 'f':
|
|
force = 1;
|
|
break;
|
|
case 'l':
|
|
tempstr = strdup(optarg);
|
|
if (read_romlayout(tempstr))
|
|
cli_classic_abort_usage();
|
|
break;
|
|
case 'i':
|
|
tempstr = strdup(optarg);
|
|
find_romentry(tempstr);
|
|
break;
|
|
case 'L':
|
|
if (++operation_specified > 1) {
|
|
fprintf(stderr, "More than one operation "
|
|
"specified. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
list_supported = 1;
|
|
break;
|
|
case 'z':
|
|
#if CONFIG_PRINT_WIKI == 1
|
|
if (++operation_specified > 1) {
|
|
fprintf(stderr, "More than one operation "
|
|
"specified. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
list_supported_wiki = 1;
|
|
#else
|
|
fprintf(stderr, "Error: Wiki output was not compiled "
|
|
"in. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
#endif
|
|
break;
|
|
case 'p':
|
|
for (programmer = 0; programmer < PROGRAMMER_INVALID; programmer++) {
|
|
name = programmer_table[programmer].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;
|
|
}
|
|
break;
|
|
case '\0':
|
|
break;
|
|
default:
|
|
/* The continue refers to the
|
|
* for loop. It is here to be
|
|
* able to differentiate between
|
|
* foo and foobar.
|
|
*/
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (programmer == PROGRAMMER_INVALID) {
|
|
fprintf(stderr, "Error: Unknown programmer "
|
|
"%s.\n", optarg);
|
|
cli_classic_abort_usage();
|
|
}
|
|
break;
|
|
case 'R':
|
|
/* print_version() is always called during startup. */
|
|
if (++operation_specified > 1) {
|
|
fprintf(stderr, "More than one operation "
|
|
"specified. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
exit(0);
|
|
break;
|
|
case 'h':
|
|
if (++operation_specified > 1) {
|
|
fprintf(stderr, "More than one operation "
|
|
"specified. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
cli_classic_usage(argv[0]);
|
|
exit(0);
|
|
break;
|
|
default:
|
|
cli_classic_abort_usage();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* FIXME: Print the actions flashrom will take. */
|
|
|
|
if (list_supported) {
|
|
print_supported();
|
|
exit(0);
|
|
}
|
|
|
|
#if CONFIG_PRINT_WIKI == 1
|
|
if (list_supported_wiki) {
|
|
print_supported_wiki();
|
|
exit(0);
|
|
}
|
|
#endif
|
|
|
|
if (optind < argc) {
|
|
fprintf(stderr, "Error: Extra parameter found.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
|
|
#if CONFIG_INTERNAL == 1
|
|
if ((programmer != PROGRAMMER_INTERNAL) && (lb_part || lb_vendor)) {
|
|
fprintf(stderr, "Error: --mainboard requires the internal "
|
|
"programmer. Aborting.\n");
|
|
cli_classic_abort_usage();
|
|
}
|
|
#endif
|
|
|
|
if (chip_to_probe) {
|
|
for (flash = flashchips; flash && flash->name; flash++)
|
|
if (!strcmp(flash->name, chip_to_probe))
|
|
break;
|
|
if (!flash || !flash->name) {
|
|
fprintf(stderr, "Error: Unknown chip '%s' specified.\n",
|
|
chip_to_probe);
|
|
printf("Run flashrom -L to view the hardware supported "
|
|
"in this flashrom version.\n");
|
|
exit(1);
|
|
}
|
|
/* Clean up after the check. */
|
|
flash = NULL;
|
|
}
|
|
|
|
/* FIXME: Delay calibration should happen in programmer code. */
|
|
myusec_calibrate_delay();
|
|
|
|
if (programmer_init(pparam)) {
|
|
fprintf(stderr, "Error: Programmer initialization failed.\n");
|
|
exit(1);
|
|
}
|
|
|
|
/* FIXME: Delay calibration should happen in programmer code. */
|
|
for (i = 0; i < ARRAY_SIZE(flashes); i++) {
|
|
flashes[i] =
|
|
probe_flash(i ? flashes[i - 1] + 1 : flashchips, 0);
|
|
if (!flashes[i])
|
|
for (i++; i < ARRAY_SIZE(flashes); i++)
|
|
flashes[i] = NULL;
|
|
}
|
|
|
|
if (flashes[1]) {
|
|
printf("Multiple flash chips were detected:");
|
|
for (i = 0; i < ARRAY_SIZE(flashes) && flashes[i]; i++)
|
|
printf(" %s", flashes[i]->name);
|
|
printf("\nPlease specify which chip to use with the -c <chipname> option.\n");
|
|
programmer_shutdown();
|
|
exit(1);
|
|
} else if (!flashes[0]) {
|
|
printf("No EEPROM/flash device found.\n");
|
|
if (!force || !chip_to_probe) {
|
|
printf("Note: flashrom can never write if the flash chip isn't found automatically.\n");
|
|
}
|
|
if (force && read_it && chip_to_probe) {
|
|
printf("Force read (-f -r -c) requested, pretending the chip is there:\n");
|
|
flashes[0] = probe_flash(flashchips, 1);
|
|
if (!flashes[0]) {
|
|
printf("Probing for flash chip '%s' failed.\n", chip_to_probe);
|
|
programmer_shutdown();
|
|
exit(1);
|
|
}
|
|
printf("Please note that forced reads most likely contain garbage.\n");
|
|
return read_flash_to_file(flashes[0], filename);
|
|
}
|
|
// FIXME: flash writes stay enabled!
|
|
programmer_shutdown();
|
|
exit(1);
|
|
}
|
|
|
|
flash = flashes[0];
|
|
|
|
check_chip_supported(flash);
|
|
|
|
size = flash->total_size * 1024;
|
|
if (check_max_decode((buses_supported & flash->bustype), size) &&
|
|
(!force)) {
|
|
fprintf(stderr, "Chip is too big for this programmer "
|
|
"(-V gives details). Use --force to override.\n");
|
|
programmer_shutdown();
|
|
return 1;
|
|
}
|
|
|
|
if (!(read_it | write_it | verify_it | erase_it)) {
|
|
printf("No operations were specified.\n");
|
|
// FIXME: flash writes stay enabled!
|
|
programmer_shutdown();
|
|
exit(0);
|
|
}
|
|
|
|
if (!filename && !erase_it) {
|
|
printf("Error: No filename specified.\n");
|
|
// FIXME: flash writes stay enabled!
|
|
programmer_shutdown();
|
|
exit(1);
|
|
}
|
|
|
|
/* Always verify write operations unless -n is used. */
|
|
if (write_it && !dont_verify_it)
|
|
verify_it = 1;
|
|
|
|
/* 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);
|
|
return doit(flash, force, filename, read_it, write_it, erase_it, verify_it);
|
|
}
|