diff options
Diffstat (limited to 'util/flashrom/flashrom.c')
-rw-r--r-- | util/flashrom/flashrom.c | 459 |
1 files changed, 459 insertions, 0 deletions
diff --git a/util/flashrom/flashrom.c b/util/flashrom/flashrom.c new file mode 100644 index 0000000000..fddefea06d --- /dev/null +++ b/util/flashrom/flashrom.c @@ -0,0 +1,459 @@ +/* + * flashrom.c: Flash programming utility + * + * Copyright 2000 Silicon Integrated System Corporation + * Copyright 2004 Tyan Corp + * yhlu yhlu@tyan.com add exclude start and end option + * Copyright 2005-2007 coresystems GmbH + * Stefan Reinauer <stepan@coresystems.de> added rom layout + * support, and checking for suitable rom image, various fixes + * support for flashing the Technologic Systems 5300. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <getopt.h> +#include <pci/pci.h> + +/* for iopl */ +#if defined (__sun) && (defined(__i386) || defined(__amd64)) +#include <strings.h> +#include <sys/sysi86.h> +#include <sys/psw.h> +#include <asm/sunddi.h> +#endif + +#include "flash.h" +#include "lbtable.h" +#include "layout.h" +#include "debug.h" + +char *chip_to_probe = NULL; +struct pci_access *pacc; /* For board and chipset_enable */ +int exclude_start_page, exclude_end_page; +int force=0, verbose=0; + +int fd_mem; + +/* + * + */ +struct pci_dev * +pci_dev_find(uint16_t vendor, uint16_t device) +{ + struct pci_dev *temp; + struct pci_filter filter; + + pci_filter_init(NULL, &filter); + filter.vendor = vendor; + filter.device = device; + + for (temp = pacc->devices; temp; temp = temp->next) + if (pci_filter_match(&filter, temp)) + return temp; + + return NULL; +} + +/* + * + */ +struct pci_dev * +pci_card_find(uint16_t vendor, uint16_t device, + uint16_t card_vendor, uint16_t card_device) +{ + struct pci_dev *temp; + struct pci_filter filter; + + pci_filter_init(NULL, &filter); + filter.vendor = vendor; + filter.device = device; + + for (temp = pacc->devices; temp; temp = temp->next) + if (pci_filter_match(&filter, temp)) { + if ((card_vendor == pci_read_word(temp, 0x2C)) && + (card_device == pci_read_word(temp, 0x2E))) + return temp; + } + + return NULL; +} + +struct flashchip *probe_flash(struct flashchip *flash) +{ + volatile uint8_t *bios; + unsigned long flash_baseaddr, size; + + while (flash->name != NULL) { + if (chip_to_probe && strcmp(flash->name, chip_to_probe) != 0) { + flash++; + continue; + } + printf_debug("Probing for %s, %d KB\n", + flash->name, flash->total_size); + + size = flash->total_size * 1024; + +#ifdef TS5300 + // FIXME: Wrong place for this decision + flash_baseaddr = 0x9400000; +#else + flash_baseaddr = (0xffffffff - size + 1); +#endif + + /* If getpagesize() > size -> + * `Error MMAP /dev/mem: Invalid argument' + * This should never happen as we don't support any flash chips + * smaller than 4k or 8k yet. + */ + + if (getpagesize() > size) { + size = getpagesize(); + printf("WARNING: size: %d -> %ld (page size)\n", + flash->total_size * 1024, (unsigned long) size); + } + + bios = mmap(0, size, PROT_WRITE | PROT_READ, MAP_SHARED, + fd_mem, (off_t)flash_baseaddr ); + if (bios == MAP_FAILED) { + perror("Error: Can't mmap " MEM_DEV "."); + exit(1); + } + flash->virt_addr = bios; + + if (flash->probe(flash) == 1) { + printf("%s found at physical address: 0x%lx\n", + flash->name, flash_baseaddr); + return flash; + } + munmap((void *) bios, size); + + flash++; + } + return NULL; +} + +int verify_flash(struct flashchip *flash, uint8_t *buf) +{ + int idx; + int total_size = flash->total_size * 1024; + volatile uint8_t *bios = flash->virt_addr; + + printf("Verifying flash "); + + if(verbose) printf("address: 0x00000000\b\b\b\b\b\b\b\b\b\b"); + + for (idx = 0; idx < total_size; idx++) { + if (verbose && ( (idx & 0xfff) == 0xfff )) + printf("0x%08x", idx); + + if (*(bios + idx) != *(buf + idx)) { + if (verbose) { + printf("0x%08x ", idx); + } + printf("- FAILED\n"); + return 1; + } + + if (verbose && ( (idx & 0xfff) == 0xfff )) + printf("\b\b\b\b\b\b\b\b\b\b"); + } + if (verbose) + printf("\b\b\b\b\b\b\b\b\b\b "); + + printf("- VERIFIED \n"); + return 0; +} + + +void usage(const char *name) +{ + printf("usage: %s [-rwvEVfh] [-c chipname] [-s exclude_start]\n", name); + printf(" [-e exclude_end] [-m vendor:part] [-l file.layout] [-i imagename] [file]\n"); + printf(" -r | --read: read flash and save into file\n" + " -w | --write: write file into flash (default when\n" + " file is specified)\n" + " -v | --verify: 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" + " -s | --estart <addr>: exclude start position\n" + " -e | --eend <addr>: exclude end postion\n" + " -m | --mainboard <vendor:part>: override mainboard settings\n" + " -f | --force: force write without checking image\n" + " -l | --layout <file.layout>: read rom layout from file\n" + " -i | --image <name>: only flash image name from flash layout\n" + "\n" + " If no file is specified, then all that happens\n" + " is that flash info is dumped.\n\n"); + exit(1); +} + +int main(int argc, char *argv[]) +{ + uint8_t *buf; + unsigned long size; + FILE *image; + struct flashchip *flash; + int opt; + int option_index = 0; + int read_it = 0, + write_it = 0, + erase_it = 0, + verify_it = 0; + int ret = 0; + + static struct option long_options[]= { + { "read", 0, 0, 'r' }, + { "write", 0, 0, 'w' }, + { "erase", 0, 0, 'E' }, + { "verify", 0, 0, 'v' }, + { "chip", 1, 0, 'c' }, + { "estart", 1, 0, 's' }, + { "eend", 1, 0, 'e' }, + { "mainboard", 1, 0, 'm' }, + { "verbose", 0, 0, 'V' }, + { "force", 0, 0, 'f' }, + { "layout", 1, 0, 'l' }, + { "image", 1, 0, 'i' }, + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } + }; + + char *filename = NULL; + + + unsigned int exclude_start_position=0, exclude_end_position=0; // [x,y) + char *tempstr=NULL, *tempstr2=NULL; + + if (argc > 1) { + /* Yes, print them. */ + int i; + printf_debug ("The arguments are:\n"); + for (i = 1; i < argc; ++i) + printf_debug ("%s\n", argv[i]); + } + + setbuf(stdout, NULL); + while ((opt = getopt_long(argc, argv, "rwvVEfc:s:e:m:l:i:h", long_options, + &option_index)) != EOF) { + switch (opt) { + case 'r': + read_it = 1; + break; + case 'w': + write_it = 1; + break; + case 'v': + verify_it = 1; + break; + case 'c': + chip_to_probe = strdup(optarg); + break; + case 'V': + verbose = 1; + break; + case 'E': + erase_it = 1; + break; + case 's': + tempstr = strdup(optarg); + sscanf(tempstr,"%x",&exclude_start_position); + break; + case 'e': + tempstr = strdup(optarg); + sscanf(tempstr,"%x",&exclude_end_position); + break; + case 'm': + tempstr = strdup(optarg); + strtok(tempstr, ":"); + tempstr2=strtok(NULL, ":"); + if (tempstr2) { + lb_vendor=tempstr; + lb_part=tempstr2; + } else { + printf("warning: ignored wrong format of" + " mainboard: %s\n", tempstr); + } + break; + case 'f': + force=1; + break; + case 'l': + tempstr=strdup(optarg); + read_romlayout(tempstr); + break; + case 'i': + tempstr=strdup(optarg); + find_romentry(tempstr); + break; + case 'h': + default: + usage(argv[0]); + break; + } + } + + if (read_it && write_it) { + printf("-r and -w are mutually exclusive\n"); + usage(argv[0]); + } + + if (optind < argc) + filename = argv[optind++]; + + /* First get full io access */ +#if defined (__sun) && (defined(__i386) || defined(__amd64)) + if (sysi86(SI86V86, V86SC_IOPL, PS_IOPL) != 0){ +#else + if (iopl(3) != 0) { +#endif + fprintf(stderr, "ERROR: iopl failed: \"%s\"\n", strerror(errno)); + exit(1); + } + + /* Initialize PCI access for flash enables */ + pacc = pci_alloc(); /* Get the pci_access structure */ + /* Set all options you want -- here we stick with the defaults */ + pci_init(pacc); /* Initialize the PCI library */ + pci_scan_bus(pacc); /* We want to get the list of devices */ + + /* Open the memory device. A lot of functions need it */ + if ((fd_mem = open(MEM_DEV, O_RDWR)) < 0) { + perror("Error: Can not access memory using " MEM_DEV ". You need to be root."); + exit(1); + } + + myusec_calibrate_delay(); + + /* We look at the lbtable first to see if we need a + * mainboard specific flash enable sequence. + */ + linuxbios_init(); + + /* try to enable it. Failure IS an option, since not all motherboards + * really need this to be done, etc., etc. + */ + ret = chipset_flash_enable(); + if (ret == -2) + printf("WARNING: No chipset found. Flash detection " + "will most likely fail.\n"); + + board_flash_enable(lb_vendor, lb_part); + + + if ((flash = probe_flash(flashchips)) == NULL) { + printf("No EEPROM/flash device found.\n"); + exit(1); + } + + printf("Flash part is %s (%d KB)\n", flash->name, flash->total_size); + + if (!filename && !erase_it) { + // FIXME: Do we really want this feature implicitly? + printf("OK, only ENABLING flash write, but NOT FLASHING.\n"); + return 0; + } + + size = flash->total_size * 1024; + buf = (uint8_t *) calloc(size, sizeof(char)); + + if (erase_it) { + printf("Erasing flash chip\n"); + flash->erase(flash); + exit(0); + } else if (read_it) { + if ((image = fopen(filename, "w")) == NULL) { + perror(filename); + exit(1); + } + printf("Reading Flash..."); + if (flash->read == NULL) + memcpy(buf, (const char *) flash->virt_addr, size); + else + flash->read(flash, buf); + + if (exclude_end_position - exclude_start_position > 0) + memset(buf+exclude_start_position, 0, + exclude_end_position-exclude_start_position); + + fwrite(buf, sizeof(char), size, image); + fclose(image); + printf("done\n"); + } else { + struct stat image_stat; + + if ((image = fopen(filename, "r")) == NULL) { + perror(filename); + exit(1); + } + if (fstat(fileno(image), &image_stat) != 0) { + perror(filename); + exit(1); + } + if(image_stat.st_size!=flash->total_size*1024) { + fprintf(stderr, "Error: Image size doesnt match\n"); + exit(1); + } + + fread(buf, sizeof(char), size, image); + show_id(buf, size); + fclose(image); + } + + /* exclude range stuff. Nice idea, but at the moment it is only + * supported in hardware by the pm49fl004 chips. + * Instead of implementing this for all chips I suggest advancing + * it to the rom layout feature below and drop exclude range + * completely once all flash chips can do rom layouts. stepan + */ + + // //////////////////////////////////////////////////////////// + if (exclude_end_position - exclude_start_position > 0) + memcpy(buf+exclude_start_position, + (const char *) flash->virt_addr+exclude_start_position, + exclude_end_position-exclude_start_position); + + exclude_start_page = exclude_start_position/flash->page_size; + if ((exclude_start_position%flash->page_size) != 0) { + exclude_start_page++; + } + exclude_end_page = exclude_end_position/flash->page_size; + // //////////////////////////////////////////////////////////// + + // This should be moved into each flash part's code to do it + // cleanly. This does the job. + handle_romentries(buf, (uint8_t *)flash->virt_addr); + + // //////////////////////////////////////////////////////////// + + if (write_it) + ret |= flash->write(flash, buf); + + if (verify_it) + ret |= verify_flash(flash, buf); + + return ret; +} |