/* * 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. */ #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <getopt.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include "ifdtool.h" #ifndef O_BINARY #define O_BINARY 0 #endif static int ifd_version; static const struct region_name region_names[MAX_REGIONS] = { { "Flash Descriptor", "fd" }, { "BIOS", "bios" }, { "Intel ME", "me" }, { "GbE", "gbe" }, { "Platform Data", "pd" }, { "Reserved", "res1" }, { "Reserved", "res2" }, { "Reserved", "res3" }, { "EC", "ec" }, }; 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; } return (fdbar_t *) (image + i); } /* * There is no version field in the descriptor so to determine * if this is a new descriptor format we check the hardcoded SPI * read frequency to see if it is fixed at 20MHz or 17MHz. */ static void check_ifd_version(char *image, int size) { fdbar_t *fdb = find_fd(image, size); fcba_t *fcba; int read_freq; if (!fdb) exit(EXIT_FAILURE); fcba = (fcba_t *) (image + (((fdb->flmap0) & 0xff) << 4)); if (!fcba) exit(EXIT_FAILURE); read_freq = (fcba->flcomp >> 17) & 7; switch (read_freq) { case SPI_FREQUENCY_20MHZ: ifd_version = IFD_VERSION_1; break; case SPI_FREQUENCY_17MHZ: ifd_version = IFD_VERSION_2; break; default: fprintf(stderr, "Unknown descriptor version: %d\n", read_freq); exit(EXIT_FAILURE); } } static region_t get_region(frba_t *frba, int region_type) { int base_mask; int limit_mask; uint32_t *flreg; region_t region; if (ifd_version >= IFD_VERSION_2) base_mask = 0x7fff; else base_mask = 0xfff; limit_mask = base_mask << 16; switch (region_type) { case 0: flreg = &frba->flreg0; break; case 1: flreg = &frba->flreg1; break; case 2: flreg = &frba->flreg2; break; case 3: flreg = &frba->flreg3; break; case 4: flreg = &frba->flreg4; break; case 5: flreg = &frba->flreg5; break; case 6: flreg = &frba->flreg6; break; case 7: flreg = &frba->flreg7; break; case 8: flreg = &frba->flreg8; break; default: fprintf(stderr, "Invalid region type %d.\n", region_type); exit (EXIT_FAILURE); } region.base = (*flreg & base_mask) << 12; region.limit = ((*flreg & limit_mask) >> 4) | 0xfff; region.size = region.limit - region.base + 1; if (region.size < 0) region.size = 0; return region; } static void set_region(frba_t *frba, int region_type, region_t region) { switch (region_type) { case 0: frba->flreg0 = (((region.limit >> 12) & 0x7fff) << 16) | ((region.base >> 12) & 0x7fff); break; case 1: frba->flreg1 = (((region.limit >> 12) & 0x7fff) << 16) | ((region.base >> 12) & 0x7fff); break; case 2: frba->flreg2 = (((region.limit >> 12) & 0x7fff) << 16) | ((region.base >> 12) & 0x7fff); break; case 3: frba->flreg3 = (((region.limit >> 12) & 0x7fff) << 16) | ((region.base >> 12) & 0x7fff); break; case 4: frba->flreg4 = (((region.limit >> 12) & 0x7fff) << 16) | ((region.base >> 12) & 0x7fff); break; default: fprintf(stderr, "Invalid region type.\n"); exit (EXIT_FAILURE); } } static const char *region_name(int region_type) { if (region_type < 0 || region_type >= MAX_REGIONS) { fprintf(stderr, "Invalid region type.\n"); exit (EXIT_FAILURE); } return region_names[region_type].pretty; } static const char *region_name_short(int region_type) { if (region_type < 0 || region_type >= MAX_REGIONS) { fprintf(stderr, "Invalid region type.\n"); exit (EXIT_FAILURE); } return region_names[region_type].terse; } static int region_num(const char *name) { int i; for (i = 0; i < MAX_REGIONS; i++) { if (strcasecmp(name, region_names[i].pretty) == 0) return i; if (strcasecmp(name, region_names[i].terse) == 0) return i; } return -1; } static const char *region_filename(int region_type) { static const char *region_filenames[MAX_REGIONS] = { "flashregion_0_flashdescriptor.bin", "flashregion_1_bios.bin", "flashregion_2_intel_me.bin", "flashregion_3_gbe.bin", "flashregion_4_platform_data.bin", "flashregion_5_reserved.bin", "flashregion_6_reserved.bin", "flashregion_7_reserved.bin", "flashregion_8_ec.bin", }; if (region_type < 0 || region_type >= MAX_REGIONS) { fprintf(stderr, "Invalid region type %d.\n", region_type); exit (EXIT_FAILURE); } return region_filenames[region_type]; } static void dump_region(int num, frba_t *frba) { region_t region = get_region(frba, num); printf(" Flash Region %d (%s): %08x - %08x %s\n", num, region_name(num), region.base, region.limit, region.size < 1 ? "(unused)" : ""); } static void dump_region_layout(char *buf, size_t bufsize, int num, frba_t *frba) { region_t region = get_region(frba, num); snprintf(buf, bufsize, "%08x:%08x %s\n", region.base, region.limit, region_name_short(num)); } static void dump_frba(frba_t * frba) { printf("Found Region Section\n"); printf("FLREG0: 0x%08x\n", frba->flreg0); dump_region(0, frba); printf("FLREG1: 0x%08x\n", frba->flreg1); dump_region(1, frba); printf("FLREG2: 0x%08x\n", frba->flreg2); dump_region(2, frba); printf("FLREG3: 0x%08x\n", frba->flreg3); dump_region(3, frba); printf("FLREG4: 0x%08x\n", frba->flreg4); dump_region(4, frba); if (ifd_version >= IFD_VERSION_2) { printf("FLREG5: 0x%08x\n", frba->flreg5); dump_region(5, frba); printf("FLREG6: 0x%08x\n", frba->flreg6); dump_region(6, frba); printf("FLREG7: 0x%08x\n", frba->flreg7); dump_region(7, frba); printf("FLREG8: 0x%08x\n", frba->flreg8); dump_region(8, frba); } } static void dump_frba_layout(frba_t * frba, char *layout_fname) { char buf[LAYOUT_LINELEN]; size_t bufsize = LAYOUT_LINELEN; int i; int layout_fd = open(layout_fname, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (layout_fd == -1) { perror("Could not open file"); exit(EXIT_FAILURE); } for (i = 0; i < MAX_REGIONS; i++) { dump_region_layout(buf, bufsize, i, frba); if (write(layout_fd, buf, strlen(buf)) < 0) { perror("Could not write to file"); exit(EXIT_FAILURE); } } close(layout_fd); printf("Wrote layout to %s\n", layout_fname); } 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_48MHZ: printf("48MHz"); break; case SPI_FREQUENCY_50MHZ_30MHZ: switch (ifd_version) { case IFD_VERSION_1: printf("50MHz"); break; case IFD_VERSION_2: printf("30MHz"); break; } break; case SPI_FREQUENCY_17MHZ: printf("17MHz"); break; default: printf("unknown<%x>MHz", freq); } } static void decode_component_density(unsigned int density) { switch (density) { case COMPONENT_DENSITY_512KB: printf("512KB"); break; case COMPONENT_DENSITY_1MB: printf("1MB"); break; case COMPONENT_DENSITY_2MB: printf("2MB"); break; case COMPONENT_DENSITY_4MB: printf("4MB"); break; case COMPONENT_DENSITY_8MB: printf("8MB"); break; case COMPONENT_DENSITY_16MB: printf("16MB"); break; case COMPONENT_DENSITY_32MB: printf("32MB"); break; case COMPONENT_DENSITY_64MB: printf("64MB"); break; case COMPONENT_DENSITY_UNUSED: printf("UNUSED"); break; default: printf("unknown<%x>MB", density); } } static void dump_fcba(fcba_t * fcba) { printf("\nFound Component Section\n"); printf("FLCOMP 0x%08x\n", fcba->flcomp); printf(" Dual Output Fast Read Support: %ssupported\n", (fcba->flcomp & (1 << 30))?"":"not "); 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 Fast Read Support: %ssupported", (fcba->flcomp & (1 << 20))?"":"not "); printf("\n Read Clock Frequency: "); decode_spi_frequency((fcba->flcomp >> 17) & 7); switch (ifd_version) { case IFD_VERSION_1: printf("\n Component 2 Density: "); decode_component_density((fcba->flcomp >> 3) & 7); printf("\n Component 1 Density: "); decode_component_density(fcba->flcomp & 7); break; case IFD_VERSION_2: printf("\n Component 2 Density: "); decode_component_density((fcba->flcomp >> 4) & 0xf); printf("\n Component 1 Density: "); decode_component_density(fcba->flcomp & 0xf); break; } printf("\n"); printf("FLILL 0x%08x\n", fcba->flill); printf(" Invalid Instruction 3: 0x%02x\n", (fcba->flill >> 24) & 0xff); printf(" Invalid Instruction 2: 0x%02x\n", (fcba->flill >> 16) & 0xff); printf(" Invalid Instruction 1: 0x%02x\n", (fcba->flill >> 8) & 0xff); printf(" Invalid Instruction 0: 0x%02x\n", fcba->flill & 0xff); printf("FLPB 0x%08x\n", fcba->flpb); printf(" Flash Partition Boundary Address: 0x%06x\n\n", (fcba->flpb & 0xfff) << 12); } static void dump_fpsba(fpsba_t * fpsba) { printf("Found 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); printf("PCHSTRP16: 0x%08x\n", fpsba->pchstrp16); printf("PCHSTRP17: 0x%08x\n\n", fpsba->pchstrp17); } static void decode_flmstr(uint32_t flmstr) { int wr_shift, rd_shift; if (ifd_version >= IFD_VERSION_2) { wr_shift = FLMSTR_WR_SHIFT_V2; rd_shift = FLMSTR_RD_SHIFT_V2; } else { wr_shift = FLMSTR_WR_SHIFT_V1; rd_shift = FLMSTR_RD_SHIFT_V1; } /* EC region access only available on v2+ */ if (ifd_version >= IFD_VERSION_2) printf(" EC Region Write Access: %s\n", (flmstr & (1 << (wr_shift + 8))) ? "enabled" : "disabled"); printf(" Platform Data Region Write Access: %s\n", (flmstr & (1 << (wr_shift + 4))) ? "enabled" : "disabled"); printf(" GbE Region Write Access: %s\n", (flmstr & (1 << (wr_shift + 3))) ? "enabled" : "disabled"); printf(" Intel ME Region Write Access: %s\n", (flmstr & (1 << (wr_shift + 2))) ? "enabled" : "disabled"); printf(" Host CPU/BIOS Region Write Access: %s\n", (flmstr & (1 << (wr_shift + 1))) ? "enabled" : "disabled"); printf(" Flash Descriptor Write Access: %s\n", (flmstr & (1 << wr_shift)) ? "enabled" : "disabled"); if (ifd_version >= IFD_VERSION_2) printf(" EC Region Read Access: %s\n", (flmstr & (1 << (rd_shift + 8))) ? "enabled" : "disabled"); printf(" Platform Data Region Read Access: %s\n", (flmstr & (1 << (rd_shift + 4))) ? "enabled" : "disabled"); printf(" GbE Region Read Access: %s\n", (flmstr & (1 << (rd_shift + 3))) ? "enabled" : "disabled"); printf(" Intel ME Region Read Access: %s\n", (flmstr & (1 << (rd_shift + 2))) ? "enabled" : "disabled"); printf(" Host CPU/BIOS Region Read Access: %s\n", (flmstr & (1 << (rd_shift + 1))) ? "enabled" : "disabled"); printf(" Flash Descriptor Read Access: %s\n", (flmstr & (1 << rd_shift)) ? "enabled" : "disabled"); /* Requestor ID doesn't exist for ifd 2 */ if (ifd_version < IFD_VERSION_2) printf(" Requester ID: 0x%04x\n\n", flmstr & 0xffff); } static void dump_fmba(fmba_t * fmba) { printf("Found Master Section\n"); printf("FLMSTR1: 0x%08x (Host CPU/BIOS)\n", fmba->flmstr1); decode_flmstr(fmba->flmstr1); printf("FLMSTR2: 0x%08x (Intel ME)\n", fmba->flmstr2); decode_flmstr(fmba->flmstr2); printf("FLMSTR3: 0x%08x (GbE)\n", fmba->flmstr3); decode_flmstr(fmba->flmstr3); if (ifd_version >= IFD_VERSION_2) { printf("FLMSTR5: 0x%08x (EC)\n", fmba->flmstr5); decode_flmstr(fmba->flmstr5); } } static void dump_fmsba(fmsba_t * fmsba) { printf("Found 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_jid(uint32_t jid) { printf(" SPI Componend Device ID 1: 0x%02x\n", (jid >> 16) & 0xff); printf(" SPI Componend Device ID 0: 0x%02x\n", (jid >> 8) & 0xff); printf(" SPI Componend Vendor ID: 0x%02x\n", jid & 0xff); } static void dump_vscc(uint32_t vscc) { printf(" Lower Erase Opcode: 0x%02x\n", vscc >> 24); printf(" Lower Write Enable on Write Status: 0x%02x\n", vscc & (1 << 20) ? 0x06 : 0x50); printf(" Lower Write Status Required: %s\n", vscc & (1 << 19) ? "Yes" : "No"); printf(" Lower Write Granularity: %d bytes\n", vscc & (1 << 18) ? 64 : 1); printf(" Lower Block / Sector Erase Size: "); switch ((vscc >> 16) & 0x3) { case 0: printf("256 Byte\n"); break; case 1: printf("4KB\n"); break; case 2: printf("8KB\n"); break; case 3: printf("64KB\n"); break; } printf(" Upper Erase Opcode: 0x%02x\n", (vscc >> 8) & 0xff); printf(" Upper Write Enable on Write Status: 0x%02x\n", vscc & (1 << 4) ? 0x06 : 0x50); printf(" Upper Write Status Required: %s\n", vscc & (1 << 3) ? "Yes" : "No"); printf(" Upper Write Granularity: %d bytes\n", vscc & (1 << 2) ? 64 : 1); printf(" Upper Block / Sector Erase Size: "); switch (vscc & 0x3) { case 0: printf("256 Byte\n"); break; case 1: printf("4KB\n"); break; case 2: printf("8KB\n"); break; case 3: printf("64KB\n"); break; } } static void dump_vtba(vtba_t *vtba, int vtl) { int i; int num = (vtl >> 1) < 8 ? (vtl >> 1) : 8; printf("ME VSCC table:\n"); for (i = 0; i < num; i++) { printf(" JID%d: 0x%08x\n", i, vtba->entry[i].jid); dump_jid(vtba->entry[i].jid); printf(" VSCC%d: 0x%08x\n", i, vtba->entry[i].vscc); dump_vscc(vtba->entry[i].vscc); } printf("\n"); } static void dump_oem(uint8_t *oem) { int i, j; printf("OEM Section:\n"); for (i = 0; i < 4; i++) { printf("%02x:", i << 4); for (j = 0; j < 16; j++) printf(" %02x", oem[(i<<4)+j]); printf ("\n"); } printf ("\n"); } 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); printf(" Intel ME VSCC Table Length (VTL): %d\n", (fdb->flumap1 >> 8) & 0xff); printf(" Intel ME VSCC Table Base Address (VTBA): 0x%06x\n\n", (fdb->flumap1 & 0xff) << 4); dump_vtba((vtba_t *) (image + ((fdb->flumap1 & 0xff) << 4)), (fdb->flumap1 >> 8) & 0xff); dump_oem((uint8_t *)image + 0xf00); 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 dump_layout(char *image, int size, char *layout_fname) { fdbar_t *fdb = find_fd(image, size); if (!fdb) exit(EXIT_FAILURE); dump_frba_layout((frba_t *) (image + (((fdb->flmap0 >> 16) & 0xff) << 4)), layout_fname); } static void write_regions(char *image, int size) { int i; int max_regions = MAX_REGIONS; fdbar_t *fdb = find_fd(image, size); if (!fdb) exit(EXIT_FAILURE); frba_t *frba = (frba_t *) (image + (((fdb->flmap0 >> 16) & 0xff) << 4)); /* Older descriptor images have fewer regions */ if (ifd_version < IFD_VERSION_2) max_regions = MAX_REGIONS_OLD; for (i = 0; i < max_regions; i++) { region_t region = get_region(frba, i); dump_region(i, frba); if (region.size > 0) { int region_fd; region_fd = open(region_filename(i), O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (region_fd < 0) { perror("Error while trying to open file"); exit(EXIT_FAILURE); } if (write(region_fd, image + region.base, region.size) != region.size) perror("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; // - 5: leave room for ".new\0" strncpy(new_filename, filename, FILENAME_MAX - 5); 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 | O_BINARY, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (new_fd < 0) { perror("Error while trying to open file"); exit(EXIT_FAILURE); } if (write(new_fd, image, size) != size) perror("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); } static void set_em100_mode(char *filename, char *image, int size) { fdbar_t *fdb = find_fd(image, size); fcba_t *fcba = (fcba_t *) (image + (((fdb->flmap0) & 0xff) << 4)); int freq; switch (ifd_version) { case IFD_VERSION_1: freq = SPI_FREQUENCY_20MHZ; break; case IFD_VERSION_2: freq = SPI_FREQUENCY_17MHZ; break; default: freq = SPI_FREQUENCY_17MHZ; break; } fcba->flcomp &= ~(1 << 30); set_spi_frequency(filename, image, size, freq); } static void lock_descriptor(char *filename, char *image, int size) { int wr_shift, rd_shift; fdbar_t *fdb = find_fd(image, size); fmba_t *fmba = (fmba_t *) (image + (((fdb->flmap1) & 0xff) << 4)); /* TODO: Dynamically take Platform Data Region and GbE Region * into regard. */ if (ifd_version >= IFD_VERSION_2) { wr_shift = FLMSTR_WR_SHIFT_V2; rd_shift = FLMSTR_RD_SHIFT_V2; /* Clear non-reserved bits */ fmba->flmstr1 &= 0xff; fmba->flmstr2 &= 0xff; fmba->flmstr3 &= 0xff; } else { wr_shift = FLMSTR_WR_SHIFT_V1; rd_shift = FLMSTR_RD_SHIFT_V1; fmba->flmstr1 = 0; fmba->flmstr2 = 0; /* Requestor ID */ fmba->flmstr3 = 0x118; } /* CPU/BIOS can read descriptor, BIOS, and GbE. */ fmba->flmstr1 |= 0xb << rd_shift; /* CPU/BIOS can write BIOS and GbE. */ fmba->flmstr1 |= 0xa << wr_shift; /* ME can read descriptor, ME, and GbE. */ fmba->flmstr2 |= 0xd << rd_shift; /* ME can write ME and GbE. */ fmba->flmstr2 |= 0xc << wr_shift; /* GbE can write only GbE. */ fmba->flmstr3 |= 0x8 << rd_shift; /* GbE can read only GbE. */ fmba->flmstr3 |= 0x8 << wr_shift; write_image(filename, image, size); } static void unlock_descriptor(char *filename, char *image, int size) { fdbar_t *fdb = find_fd(image, size); fmba_t *fmba = (fmba_t *) (image + (((fdb->flmap1) & 0xff) << 4)); if (ifd_version >= IFD_VERSION_2) { /* Access bits for each region are read: 19:8 write: 31:20 */ fmba->flmstr1 = 0xffffff00 | (fmba->flmstr1 & 0xff); fmba->flmstr2 = 0xffffff00 | (fmba->flmstr2 & 0xff); fmba->flmstr3 = 0xffffff00 | (fmba->flmstr3 & 0xff); } else { fmba->flmstr1 = 0xffff0000; fmba->flmstr2 = 0xffff0000; fmba->flmstr3 = 0x08080118; } 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 | O_BINARY); 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 (size < region.base + offset + region_size) { fprintf(stderr, "Output file is too small. (%d < %d)\n", size, region.base + offset + region_size); exit(EXIT_FAILURE); } 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); } unsigned int next_pow2(unsigned int x) { unsigned int y = 1; if (x == 0) return 0; while (y <= x) y = y << 1; return y; } /** * Determine if two memory regions overlap. * * @param r1, r2 Memory regions to compare. * @return 0 if the two regions are seperate * @return 1 if the two regions overlap */ static int regions_collide(region_t r1, region_t r2) { if ((r1.size == 0) || (r2.size == 0)) return 0; if ( ((r1.base >= r2.base) && (r1.base <= r2.limit)) || ((r1.limit >= r2.base) && (r1.limit <= r2.limit)) ) return 1; return 0; } void new_layout(char *filename, char *image, int size, char *layout_fname) { FILE *romlayout; char tempstr[256]; char layout_region_name[256]; int i, j; int region_number; region_t current_regions[MAX_REGIONS]; region_t new_regions[MAX_REGIONS]; int new_extent = 0; char *new_image; /* load current descriptor map and regions */ 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 < MAX_REGIONS; i++) { current_regions[i] = get_region(frba, i); new_regions[i] = get_region(frba, i); } /* read new layout */ romlayout = fopen(layout_fname, "r"); if (!romlayout) { perror("Could not read layout file.\n"); exit(EXIT_FAILURE); } while (!feof(romlayout)) { char *tstr1, *tstr2; if (2 != fscanf(romlayout, "%255s %255s\n", tempstr, layout_region_name)) continue; region_number = region_num(layout_region_name); if (region_number < 0) continue; tstr1 = strtok(tempstr, ":"); tstr2 = strtok(NULL, ":"); if (!tstr1 || !tstr2) { fprintf(stderr, "Could not parse layout file.\n"); exit(EXIT_FAILURE); } new_regions[region_number].base = strtol(tstr1, (char **)NULL, 16); new_regions[region_number].limit = strtol(tstr2, (char **)NULL, 16); new_regions[region_number].size = new_regions[region_number].limit - new_regions[region_number].base + 1; if (new_regions[region_number].size < 0) new_regions[region_number].size = 0; } fclose(romlayout); /* check new layout */ for (i = 0; i < MAX_REGIONS; i++) { if (new_regions[i].size == 0) continue; if (new_regions[i].size < current_regions[i].size) { printf("DANGER: Region %s is shrinking.\n", region_name(i)); printf(" The region will be truncated to fit.\n"); printf(" This may result in an unusable image.\n"); } for (j = i + 1; j < MAX_REGIONS; j++) { if (regions_collide(new_regions[i], new_regions[j])) { fprintf(stderr, "Regions would overlap.\n"); exit(EXIT_FAILURE); } } /* detect if the image size should grow */ if (new_extent < new_regions[i].limit) new_extent = new_regions[i].limit; } new_extent = next_pow2(new_extent - 1); if (new_extent != size) { printf("The image has changed in size.\n"); printf("The old image is %d bytes.\n", size); printf("The new image is %d bytes.\n", new_extent); } /* copy regions to a new image */ new_image = malloc(new_extent); memset(new_image, 0xff, new_extent); for (i = 0; i < MAX_REGIONS; i++) { int copy_size = new_regions[i].size; int offset_current = 0, offset_new = 0; region_t current = current_regions[i]; region_t new = new_regions[i]; if (new.size == 0) continue; if (new.size > current.size) { /* copy from the end of the current region */ copy_size = current.size; offset_new = new.size - current.size; } if (new.size < current.size) { /* copy to the end of the new region */ offset_current = current.size - new.size; } printf("Copy Descriptor %d (%s) (%d bytes)\n", i, region_name(i), copy_size); printf(" from %08x+%08x:%08x (%10d)\n", current.base, offset_current, current.limit, current.size); printf(" to %08x+%08x:%08x (%10d)\n", new.base, offset_new, new.limit, new.size); memcpy(new_image + new.base + offset_new, image + current.base + offset_current, copy_size); } /* update new descriptor regions */ fdb = find_fd(new_image, new_extent); if (!fdb) exit(EXIT_FAILURE); frba = (frba_t *) (new_image + (((fdb->flmap0 >> 16) & 0xff) << 4)); for (i = 1; i < MAX_REGIONS; i++) { set_region(frba, i, new_regions[i]); } write_image(filename, new_image, new_extent); free(new_image); } 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 <http://www.gnu.org/licenses/>.\n\n"); } static void print_usage(const char *name) { printf("usage: %s [-vhdix?] <filename>\n", name); printf("\n" " -d | --dump: dump intel firmware descriptor\n" " -f | --layout <filename> dump regions into a flashrom layout file\n" " -x | --extract: extract intel fd modules\n" " -i | --inject <region>:<module> inject file <module> into region <region>\n" " -n | --newlayout <filename> update regions using a flashrom layout file\n" " -s | --spifreq <17|20|30|33|48|50> set the SPI frequency\n" " -e | --em100 set SPI frequency to 20MHz and disable\n" " Dual Output Fast Read Support\n" " -l | --lock Lock firmware descriptor and ME region\n" " -u | --unlock Unlock firmware descriptor and ME region\n" " -v | --version: print the version\n" " -h | --help: print this help\n\n" "<region> 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; int mode_em100 = 0, mode_locked = 0, mode_unlocked = 0; int mode_layout = 0, mode_newlayout = 0; char *region_type_string = NULL, *region_fname = NULL, *layout_fname = NULL; int region_type = -1, inputfreq = 0; enum spi_frequency spifreq = SPI_FREQUENCY_20MHZ; static struct option long_options[] = { {"dump", 0, NULL, 'd'}, {"layout", 1, NULL, 'f'}, {"extract", 0, NULL, 'x'}, {"inject", 1, NULL, 'i'}, {"newlayout", 1, NULL, 'n'}, {"spifreq", 1, NULL, 's'}, {"em100", 0, NULL, 'e'}, {"lock", 0, NULL, 'l'}, {"unlock", 0, NULL, 'u'}, {"version", 0, NULL, 'v'}, {"help", 0, NULL, 'h'}, {0, 0, 0, 0} }; while ((opt = getopt_long(argc, argv, "df:xi:n:s:eluvh?", long_options, &option_index)) != EOF) { switch (opt) { case 'd': mode_dump = 1; break; case 'f': mode_layout = 1; layout_fname = strdup(optarg); if (!layout_fname) { fprintf(stderr, "No layout file specified\n"); print_usage(argv[0]); exit(EXIT_FAILURE); } 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; else if (!strcasecmp("EC", region_type_string)) region_type = 8; 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 'n': mode_newlayout = 1; layout_fname = strdup(optarg); if (!layout_fname) { fprintf(stderr, "No layout file specified\n"); print_usage(argv[0]); exit(EXIT_FAILURE); } break; case 's': // Parse the requested SPI frequency inputfreq = strtol(optarg, NULL, 0); switch (inputfreq) { case 17: spifreq = SPI_FREQUENCY_17MHZ; break; case 20: spifreq = SPI_FREQUENCY_20MHZ; break; case 30: spifreq = SPI_FREQUENCY_50MHZ_30MHZ; break; case 33: spifreq = SPI_FREQUENCY_33MHZ; break; case 48: spifreq = SPI_FREQUENCY_48MHZ; break; case 50: spifreq = SPI_FREQUENCY_50MHZ_30MHZ; break; default: fprintf(stderr, "Invalid SPI Frequency: %d\n", inputfreq); print_usage(argv[0]); exit(EXIT_FAILURE); } mode_spifreq = 1; break; case 'e': mode_em100 = 1; break; case 'l': mode_locked = 1; if (mode_unlocked == 1) { fprintf(stderr, "Locking/Unlocking FD and ME are mutually exclusive\n"); exit(EXIT_FAILURE); } break; case 'u': mode_unlocked = 1; if (mode_locked == 1) { fprintf(stderr, "Locking/Unlocking FD and ME are mutually exclusive\n"); exit(EXIT_FAILURE); } 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_layout + mode_extract + mode_inject + mode_newlayout + (mode_spifreq | mode_em100 | mode_unlocked | mode_locked)) > 1) { fprintf(stderr, "You may not specify more than one mode.\n\n"); print_usage(argv[0]); exit(EXIT_FAILURE); } if ((mode_dump + mode_layout + mode_extract + mode_inject + mode_newlayout + mode_spifreq + mode_em100 + mode_locked + mode_unlocked) == 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 | O_BINARY); 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); check_ifd_version(image, size); if (mode_dump) dump_fd(image, size); if (mode_layout) dump_layout(image, size, layout_fname); if (mode_extract) write_regions(image, size); if (mode_inject) inject_region(filename, image, size, region_type, region_fname); if (mode_newlayout) new_layout(filename, image, size, layout_fname); if (mode_spifreq) set_spi_frequency(filename, image, size, spifreq); if (mode_em100) set_em100_mode(filename, image, size); if(mode_locked) lock_descriptor(filename, image, size); if (mode_unlocked) unlock_descriptor(filename, image, size); free(image); return 0; }