From 1c795ad109bd382ff75e92d83f5721b8ed7c3be1 Mon Sep 17 00:00:00 2001 From: Stefan Reinauer Date: Fri, 14 Oct 2011 12:49:41 -0700 Subject: [PATCH] Add ifdtool, utility to read / modify Intel Firmware Descriptor images Change-Id: Ie78b97bf573d238d0dff9a663e774deb1b7dea44 Signed-off-by: Stefan Reinauer Reviewed-on: http://review.coreboot.org/272 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi --- util/ifdtool/Makefile | 53 ++++ util/ifdtool/ifdtool.c | 551 +++++++++++++++++++++++++++++++++++++++++ util/ifdtool/ifdtool.h | 87 +++++++ 3 files changed, 691 insertions(+) create mode 100644 util/ifdtool/Makefile create mode 100644 util/ifdtool/ifdtool.c create mode 100644 util/ifdtool/ifdtool.h diff --git a/util/ifdtool/Makefile b/util/ifdtool/Makefile new file mode 100644 index 0000000000..fc8581f280 --- /dev/null +++ b/util/ifdtool/Makefile @@ -0,0 +1,53 @@ +# +# ifdtool - dump Intel Firmware Descriptor information +# +# Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. +# +# 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; version 2 of the License. +# +# 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 +# + +PROGRAM = ifdtool + +CC = gcc +INSTALL = /usr/bin/install +PREFIX = /usr/local +CFLAGS = -O2 -g -Wall -W +LDFLAGS = + +OBJS = ifdtool.o + +all: dep $(PROGRAM) + +$(PROGRAM): $(OBJS) + $(CC) -o $(PROGRAM) $(OBJS) $(LDFLAGS) + +clean: + rm -f $(PROGRAM) *.o *~ + +distclean: clean + rm -f .dependencies + +dep: + @$(CC) $(CFLAGS) -MM *.c > .dependencies + +install: $(PROGRAM) + mkdir -p $(DESTDIR)$(PREFIX)/bin + $(INSTALL) $(PROGRAM) $(DESTDIR)$(PREFIX)/bin + mkdir -p $(DESTDIR)$(PREFIX)/share/man/man8 + $(INSTALL) $(PROGRAM).8 $(DESTDIR)$(PREFIX)/share/man/man8 + +.PHONY: all clean distclean dep + +-include .dependencies + diff --git a/util/ifdtool/ifdtool.c b/util/ifdtool/ifdtool.c new file mode 100644 index 0000000000..204c4492dc --- /dev/null +++ b/util/ifdtool/ifdtool.c @@ -0,0 +1,551 @@ +/* + * ifdtool - dump Intel Firmware Descriptor information + * + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * 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; version 2 of the License. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include "ifdtool.h" + +static fdbar_t *find_fd(char *image, int size) +{ + int i, found = 0; + + /* Scan for FD signature */ + for (i = 0; i < (size - 4); i += 4) { + if (*(uint32_t *) (image + i) == 0x0FF0A55A) { + found = 1; + break; // signature found. + } + } + + if (!found) { + printf("No Flash Descriptor found in this image\n"); + return NULL; + } + + printf("Found Flash Descriptor signature at 0x%08x\n", i); + + return (fdbar_t *) (image + i); +} + +typedef struct { + int base, limit, size; +} region_t; + +static region_t get_region(frba_t *frba, int region_type) +{ + region_t region; + region.base = 0, region.limit = 0, region.size = 0; + + switch (region_type) { + case 0: + region.base = (frba->flreg0 & 0x00000fff) << 12; + region.limit = ((frba->flreg0 & 0x0fff0000) >> 4) | 0xfff; + break; + case 1: + region.base = (frba->flreg1 & 0x00000fff) << 12; + region.limit = ((frba->flreg1 & 0x0fff0000) >> 4) | 0xfff; + break; + case 2: + region.base = (frba->flreg2 & 0x00000fff) << 12; + region.limit = ((frba->flreg2 & 0x0fff0000) >> 4) | 0xfff; + break; + case 3: + region.base = (frba->flreg3 & 0x00000fff) << 12; + region.limit = ((frba->flreg3 & 0x0fff0000) >> 4) | 0xfff; + break; + case 4: + region.base = (frba->flreg4 & 0x00000fff) << 12; + region.limit = ((frba->flreg4 & 0x0fff0000) >> 4) | 0xfff; + break; + default: + fprintf(stderr, "Invalid region type.\n"); + exit (EXIT_FAILURE); + } + + region.size = region.limit - region.base + 1; + + return region; +} + +static const char *region_name(int region_type) +{ + static const char *regions[5] = { + "Flash Descriptor", + "BIOS", + "Intel ME", + "GbE", + "Platform Data" + }; + + if (region_type < 0 || region_type > 4) { + fprintf(stderr, "Invalid region type.\n"); + exit (EXIT_FAILURE); + } + + return regions[region_type]; +} + +static const char *region_filename(int region_type) +{ + static const char *region_filenames[5] = { + "flashregion_0_flashdescriptor.bin", + "flashregion_1_bios.bin", + "flashregion_2_intel_me.bin", + "flashregion_3_gbe.bin", + "flashregion_4_platform_data.bin" + }; + + if (region_type < 0 || region_type > 4) { + fprintf(stderr, "Invalid region type.\n"); + exit (EXIT_FAILURE); + } + + return region_filenames[region_type]; +} + +static void dump_frba(frba_t * frba) +{ + printf("\nFound Region Section\n"); + printf("FLREG0: 0x%08x\n", frba->flreg0); + printf("FLREG1: 0x%08x\n", frba->flreg1); + printf("FLREG2: 0x%08x\n", frba->flreg2); + printf("FLREG3: 0x%08x\n", frba->flreg3); + printf("FLREG4: 0x%08x\n", frba->flreg4); +} + +static void decode_spi_frequency(unsigned int freq) +{ + switch (freq) { + case SPI_FREQUENCY_20MHZ: + printf("20MHz"); + break; + case SPI_FREQUENCY_33MHZ: + printf("33MHz"); + break; + case SPI_FREQUENCY_50MHZ: + printf("50MHz"); + break; + default: + printf("unknown<%x>MHz", freq); + } +} + +static void dump_fcba(fcba_t * fcba) +{ + printf("\nFound Component Section\n"); + printf("FLCOMP 0x%08x\n", fcba->flcomp); + printf(" Read ID/Read Status Clock Frequency: "); + decode_spi_frequency((fcba->flcomp >> 27) & 7); + printf("\n Write/Erase Clock Frequency: "); + decode_spi_frequency((fcba->flcomp >> 24) & 7); + printf("\n Fast Read Clock Frequency: "); + decode_spi_frequency((fcba->flcomp >> 21) & 7); + printf("\n"); + printf("FLILL 0x%08x\n", fcba->flill); + printf("FLPB 0x%08x\n", fcba->flpb); +} + +static void dump_fpsba(fpsba_t * fpsba) +{ + printf("\nFound PCH Strap Section\n"); + printf("PCHSTRP0: 0x%08x\n", fpsba->pchstrp0); + printf("PCHSTRP1: 0x%08x\n", fpsba->pchstrp1); + printf("PCHSTRP2: 0x%08x\n", fpsba->pchstrp2); + printf("PCHSTRP3: 0x%08x\n", fpsba->pchstrp3); + printf("PCHSTRP4: 0x%08x\n", fpsba->pchstrp4); + printf("PCHSTRP5: 0x%08x\n", fpsba->pchstrp5); + printf("PCHSTRP6: 0x%08x\n", fpsba->pchstrp6); + printf("PCHSTRP7: 0x%08x\n", fpsba->pchstrp7); + printf("PCHSTRP8: 0x%08x\n", fpsba->pchstrp8); + printf("PCHSTRP9: 0x%08x\n", fpsba->pchstrp9); + printf("PCHSTRP10: 0x%08x\n", fpsba->pchstrp10); + printf("PCHSTRP11: 0x%08x\n", fpsba->pchstrp11); + printf("PCHSTRP12: 0x%08x\n", fpsba->pchstrp12); + printf("PCHSTRP13: 0x%08x\n", fpsba->pchstrp13); + printf("PCHSTRP14: 0x%08x\n", fpsba->pchstrp14); + printf("PCHSTRP15: 0x%08x\n", fpsba->pchstrp15); +} + +static void dump_fmba(fmba_t * fmba) +{ + printf("\nFound Master Section\n"); + printf("FLMSTR1: 0x%08x\n", fmba->flmstr1); + printf("FLMSTR2: 0x%08x\n", fmba->flmstr2); + printf("FLMSTR3: 0x%08x\n", fmba->flmstr3); +} + +static void dump_fmsba(fmsba_t * fmsba) +{ + printf("\nFound Processor Strap Section\n"); + printf("????: 0x%08x\n", fmsba->data[0]); + printf("????: 0x%08x\n", fmsba->data[1]); + printf("????: 0x%08x\n", fmsba->data[2]); + printf("????: 0x%08x\n", fmsba->data[3]); +} + +static void dump_fd(char *image, int size) +{ + fdbar_t *fdb = find_fd(image, size); + if (!fdb) + exit(EXIT_FAILURE); + + printf("FLMAP0: 0x%08x\n", fdb->flmap0); + printf(" NR: %d\n", (fdb->flmap0 >> 24) & 7); + printf(" FRBA: 0x%x\n", ((fdb->flmap0 >> 16) & 0xff) << 4); + printf(" NC: %d\n", ((fdb->flmap0 >> 8) & 3) + 1); + printf(" FCBA: 0x%x\n", ((fdb->flmap0) & 0xff) << 4); + + printf("FLMAP1: 0x%08x\n", fdb->flmap1); + printf(" ISL: 0x%02x\n", (fdb->flmap1 >> 24) & 0xff); + printf(" FPSBA: 0x%x\n", ((fdb->flmap1 >> 16) & 0xff) << 4); + printf(" NM: %d\n", (fdb->flmap1 >> 8) & 3); + printf(" FMBA: 0x%x\n", ((fdb->flmap1) & 0xff) << 4); + + printf("FLMAP2: 0x%08x\n", fdb->flmap2); + printf(" PSL: 0x%04x\n", (fdb->flmap2 >> 8) & 0xffff); + printf(" FMSBA: 0x%x\n", ((fdb->flmap2) & 0xff) << 4); + + printf("FLUMAP1: 0x%08x\n", fdb->flumap1); + + dump_frba((frba_t *) + (image + (((fdb->flmap0 >> 16) & 0xff) << 4))); + dump_fcba((fcba_t *) (image + (((fdb->flmap0) & 0xff) << 4))); + dump_fpsba((fpsba_t *) + (image + (((fdb->flmap1 >> 16) & 0xff) << 4))); + dump_fmba((fmba_t *) (image + (((fdb->flmap1) & 0xff) << 4))); + dump_fmsba((fmsba_t *) (image + (((fdb->flmap2) & 0xff) << 4))); +} + +static void write_regions(char *image, int size) +{ + int i; + + fdbar_t *fdb = find_fd(image, size); + if (!fdb) + exit(EXIT_FAILURE); + + frba_t *frba = + (frba_t *) (image + (((fdb->flmap0 >> 16) & 0xff) << 4)); + + for (i = 0; i<5; i++) { + region_t region = get_region(frba, i); + printf("Flash Region %d (%s): %08x - %08x %s\n", + i, region_name(i), region.base, region.limit, + region.size < 1 ? "(unused)" : ""); + if (region.size > 0) { + int region_fd; + region_fd = open(region_filename(i), + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (write(region_fd, image + region.base, region.size) != region.size) + printf("Error while writing."); + close(region_fd); + } + } +} + +static void write_image(char *filename, char *image, int size) +{ + char new_filename[FILENAME_MAX]; // allow long file names + int new_fd; + + strncpy(new_filename, filename, FILENAME_MAX); + strncat(new_filename, ".new", FILENAME_MAX - strlen(filename)); + + printf("Writing new image to %s\n", new_filename); + + // Now write out new image + new_fd = open(new_filename, + O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (write(new_fd, image, size) != size) + printf("Error while writing."); + close(new_fd); +} + +static void set_spi_frequency(char *filename, char *image, int size, + enum spi_frequency freq) +{ + fdbar_t *fdb = find_fd(image, size); + fcba_t *fcba = (fcba_t *) (image + (((fdb->flmap0) & 0xff) << 4)); + + /* clear bits 21-29 */ + fcba->flcomp &= ~0x3fe00000; + /* Read ID and Read Status Clock Frequency */ + fcba->flcomp |= freq << 27; + /* Write and Erase Clock Frequency */ + fcba->flcomp |= freq << 24; + /* Fast Read Clock Frequency */ + fcba->flcomp |= freq << 21; + + write_image(filename, image, size); +} + +void inject_region(char *filename, char *image, int size, int region_type, + char *region_fname) +{ + fdbar_t *fdb = find_fd(image, size); + if (!fdb) + exit(EXIT_FAILURE); + frba_t *frba = + (frba_t *) (image + (((fdb->flmap0 >> 16) & 0xff) << 4)); + + region_t region = get_region(frba, region_type); + if (region.size <= 0xfff) { + fprintf(stderr, "Region %s is disabled in target. Not injecting.\n", + region_name(region_type)); + exit(EXIT_FAILURE); + } + + int region_fd = open(region_fname, O_RDONLY); + if (region_fd == -1) { + perror("Could not open file"); + exit(EXIT_FAILURE); + } + struct stat buf; + if (fstat(region_fd, &buf) == -1) { + perror("Could not stat file"); + exit(EXIT_FAILURE); + } + int region_size = buf.st_size; + + printf("File %s is %d bytes\n", region_fname, region_size); + + if ( (region_size > region.size) || ((region_type != 1) && + (region_size != region.size))) { + fprintf(stderr, "Region %s is %d(0x%x) bytes. File is %d(0x%x)" + " bytes. Not injecting.\n", + region_name(region_type), region.size, + region.size, region_size, region_size); + exit(EXIT_FAILURE); + } + + int offset = 0; + if ((region_type == 1) && (region_size < region.size)) { + fprintf(stderr, "Region %s is %d(0x%x) bytes. File is %d(0x%x)" + " bytes. Padding before injecting.\n", + region_name(region_type), region.size, + region.size, region_size, region_size); + offset = region.size - region_size; + memset(image + region.base, 0xff, offset); + } + + if (read(region_fd, image + region.base + offset, region_size) + != region_size) { + perror("Could not read file"); + exit(EXIT_FAILURE); + } + + close(region_fd); + + printf("Adding %s as the %s section of %s\n", + region_fname, region_name(region_type), filename); + write_image(filename, image, size); +} + +static void print_version(void) +{ + printf("ifdtool v%s -- ", IFDTOOL_VERSION); + printf("Copyright (C) 2011 Google Inc.\n\n"); + printf + ("This program is free software: you can redistribute it and/or modify\n" + "it under the terms of the GNU General Public License as published by\n" + "the Free Software Foundation, version 2 of the License.\n\n" + "This program is distributed in the hope that it will be useful,\n" + "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" + "GNU General Public License for more details.\n\n" + "You should have received a copy of the GNU General Public License\n" + "along with this program. If not, see .\n\n"); +} + +static void print_usage(const char *name) +{ + printf("usage: %s [-vhdix?] \n", name); + printf("\n" + " -d | --dump: dump intel firmware descriptor\n" + " -x | --extract: extract intel fd modules\n" + " -i | --inject : inject file into region \n" + " -s | --spifreq <20|33|50> set the SPI frequency\n" + " -v | --version: print the version\n" + " -h | --help: print this help\n\n" + " is one of Descriptor, BIOS, ME, GbE, Platform\n" + "\n"); +} + +int main(int argc, char *argv[]) +{ + int opt, option_index = 0; + int mode_dump = 0, mode_extract = 0, mode_inject = 0, mode_spifreq = 0; + char *region_type_string = NULL, *region_fname = NULL; + int region_type = -1, inputfreq = 0; + enum spi_frequency spifreq = SPI_FREQUENCY_20MHZ; + + static struct option long_options[] = { + {"dump", 0, NULL, 'd'}, + {"extract", 0, NULL, 'x'}, + {"inject", 1, NULL, 'i'}, + {"spifreq", 1, NULL, 's'}, + {"version", 0, NULL, 'v'}, + {"help", 0, NULL, 'h'}, + {0, 0, 0, 0} + }; + + while ((opt = getopt_long(argc, argv, "dxi:s:vh?", + long_options, &option_index)) != EOF) { + switch (opt) { + case 'd': + mode_dump = 1; + break; + case 'x': + mode_extract = 1; + break; + case 'i': + // separate type and file name + region_type_string = strdup(optarg); + region_fname = strchr(region_type_string, ':'); + if (!region_fname) { + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + region_fname[0] = '\0'; + region_fname++; + // Descriptor, BIOS, ME, GbE, Platform + // valid type? + if (!strcasecmp("Descriptor", region_type_string)) + region_type = 0; + else if (!strcasecmp("BIOS", region_type_string)) + region_type = 1; + else if (!strcasecmp("ME", region_type_string)) + region_type = 2; + else if (!strcasecmp("GbE", region_type_string)) + region_type = 3; + else if (!strcasecmp("Platform", region_type_string)) + region_type = 4; + if (region_type == -1) { + fprintf(stderr, "No such region type: '%s'\n\n", + region_type_string); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + mode_inject = 1; + break; + case 's': + // Parse the requested SPI frequency + inputfreq = strtol(optarg, NULL, 0); + switch (inputfreq) { + case 20: + spifreq = SPI_FREQUENCY_20MHZ; + break; + case 33: + spifreq = SPI_FREQUENCY_33MHZ; + break; + case 50: + spifreq = SPI_FREQUENCY_50MHZ; + break; + default: + fprintf(stderr, "Invalid SPI Frequency: %d\n", + inputfreq); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + mode_spifreq = 1; + break; + case 'v': + print_version(); + exit(EXIT_SUCCESS); + break; + case 'h': + case '?': + default: + print_usage(argv[0]); + exit(EXIT_SUCCESS); + break; + } + } + + if ((mode_dump + mode_extract + mode_inject + mode_spifreq) > 1) { + fprintf(stderr, "Only one mode allowed.\n\n"); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + + if ((mode_dump + mode_extract + mode_inject + mode_spifreq) == 0) { + fprintf(stderr, "You need to specify a mode.\n\n"); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + + if (optind + 1 != argc) { + fprintf(stderr, "You need to specify a file.\n\n"); + print_usage(argv[0]); + exit(EXIT_FAILURE); + } + + char *filename = argv[optind]; + int bios_fd = open(filename, O_RDONLY); + if (bios_fd == -1) { + perror("Could not open file"); + exit(EXIT_FAILURE); + } + struct stat buf; + if (fstat(bios_fd, &buf) == -1) { + perror("Could not stat file"); + exit(EXIT_FAILURE); + } + int size = buf.st_size; + + printf("File %s is %d bytes\n", filename, size); + + char *image = malloc(size); + if (!image) { + printf("Out of memory.\n"); + exit(EXIT_FAILURE); + } + + if (read(bios_fd, image, size) != size) { + perror("Could not read file"); + exit(EXIT_FAILURE); + } + + close(bios_fd); + + if (mode_dump) + dump_fd(image, size); + + if (mode_extract) + write_regions(image, size); + + if (mode_inject) + inject_region(filename, image, size, region_type, + region_fname); + + if (mode_spifreq) + set_spi_frequency(filename, image, size, spifreq); + + free(image); + + return 0; +} diff --git a/util/ifdtool/ifdtool.h b/util/ifdtool/ifdtool.h new file mode 100644 index 0000000000..057153434d --- /dev/null +++ b/util/ifdtool/ifdtool.h @@ -0,0 +1,87 @@ +/* + * ifdtool - dump Intel Firmware Descriptor information + * + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * 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; version 2 of the License. + * + * 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 +#define IFDTOOL_VERSION "1.0" + +enum spi_frequency { + SPI_FREQUENCY_20MHZ = 0, + SPI_FREQUENCY_33MHZ = 1, + SPI_FREQUENCY_50MHZ = 4, +}; + +// flash descriptor +typedef struct { + uint32_t flvalsig; + uint32_t flmap0; + uint32_t flmap1; + uint32_t flmap2; + uint8_t reserved[0xefc - 0x20]; + uint32_t flumap1; +} __attribute__((packed)) fdbar_t; + +// regions +typedef struct { + uint32_t flreg0; + uint32_t flreg1; + uint32_t flreg2; + uint32_t flreg3; + uint32_t flreg4; +} __attribute__((packed)) frba_t; + +// component section +typedef struct { + uint32_t flcomp; + uint32_t flill; + uint32_t flpb; +} __attribute__((packed)) fcba_t; + +// pch strap +typedef struct { + uint32_t pchstrp0; + uint32_t pchstrp1; + uint32_t pchstrp2; + uint32_t pchstrp3; + uint32_t pchstrp4; + uint32_t pchstrp5; + uint32_t pchstrp6; + uint32_t pchstrp7; + uint32_t pchstrp8; + uint32_t pchstrp9; + uint32_t pchstrp10; + uint32_t pchstrp11; + uint32_t pchstrp12; + uint32_t pchstrp13; + uint32_t pchstrp14; + uint32_t pchstrp15; +} __attribute__((packed)) fpsba_t; + +// master +typedef struct { + uint32_t flmstr1; + uint32_t flmstr2; + uint32_t flmstr3; +} __attribute__((packed)) fmba_t; + +// processor strap +typedef struct { + uint32_t data[8]; +} __attribute__((packed)) fmsba_t; + +