1
0
mirror of https://review.coreboot.org/flashrom.git synced 2025-04-27 07:02:34 +02:00
flashrom/cli_classic.c
David Hendricks 8bb2021d77 Use shutdown callback mechanism to shutdown programmers
This patch attempts to resolve some programmer shutdown ordering issues
by having the programmer init functions register shutdown callbacks explicitly
wherever it makes most sense. Before, assumptions were made that could lead to
the internal programmer's state changing before the external programmer could be
shut down properly. Now, each programmer cleans up after itself and (hopefully)
performs each operation in the correct order.

As a side-effect, this patch gives us a better usage model for reverse
operations such as rpci_* and rmmio_*. In the long-run, this should make
reversing the initialization process easier to understand, less tedious, and
less error-prone.

In short, this patch does the following:
- Registers a shutdown callback during initialization for each programmer.
- Kills the .shutdown function pointer from programmer_entry struct. Also,
  make most shutdown functions static.
- Adds a few minor clean-ups and corrections (e.g. missing physunmap() calls).

TODO: Remove forward declaration of serprog_shutdown() (added to simplify diff)

Corresponding to flashrom svn r1338.

Signed-off-by: David Hendricks <dhendrix@google.com>
Acked-by: Carl-Daniel Hailfinger <c-d.hailfinger.devel.2006@gmx.net>
2011-06-14 01:35:36 +00:00

441 lines
12 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/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)
{
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\n");
list_programmers_linebreak(37, 80, 1);
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. */
const struct flashchip *flash;
struct flashchip flashes[3];
struct flashchip *fill_flash;
int startchip = 0;
int chipcount = 0;
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;
static const char optstring[] = "r:Rw:v:nVEfc:m:l:i:p:Lzh";
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'},
{"chip", 1, NULL, 'c'},
{"mainboard", 1, NULL, 'm'},
{"verbose", 0, NULL, 'V'},
{"force", 0, NULL, 'f'},
{"layout", 1, NULL, 'l'},
{"image", 1, NULL, 'i'},
{"list-supported", 0, NULL, 'L'},
{"list-supported-wiki", 0, NULL, 'z'},
{"programmer", 1, NULL, 'p'},
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'R'},
{NULL, 0, NULL, 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");
programmer_shutdown();
exit(1);
}
for (i = 0; i < ARRAY_SIZE(flashes); i++) {
startchip = probe_flash(startchip, &flashes[i], 0);
if (startchip == -1)
break;
chipcount++;
startchip++;
}
if (chipcount > 1) {
printf("Multiple flash chips were detected:");
for (i = 0; i < chipcount; 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 (!chipcount) {
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");
startchip = probe_flash(0, &flashes[0], 1);
if (startchip == -1) {
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);
}
fill_flash = &flashes[0];
check_chip_supported(fill_flash);
size = fill_flash->total_size * 1024;
if (check_max_decode((buses_supported & fill_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(fill_flash, force, filename, read_it, write_it, erase_it, verify_it);
}