| /* | 
 |  * 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; | 
 |  | 
 | 	return doit(flash, force, filename, read_it, write_it, erase_it, verify_it); | 
 | } |