diff options
-rw-r--r-- | util/cbfstool/Makefile | 2 | ||||
-rw-r--r-- | util/cbfstool/Makefile.inc | 1 | ||||
-rw-r--r-- | util/cbfstool/cbfstool.c | 39 | ||||
-rw-r--r-- | util/cbfstool/fit.c | 277 | ||||
-rw-r--r-- | util/cbfstool/fit.h | 25 |
5 files changed, 343 insertions, 1 deletions
diff --git a/util/cbfstool/Makefile b/util/cbfstool/Makefile index a14a76f89c..a51e6de590 100644 --- a/util/cbfstool/Makefile +++ b/util/cbfstool/Makefile @@ -7,7 +7,7 @@ CFLAGS += -D_7ZIP_ST BINARY:=$(obj)/cbfstool -COMMON:=cbfstool.o common.o cbfs_image.o compress.o +COMMON:=cbfstool.o common.o cbfs_image.o compress.o fit.o COMMON+=cbfs-mkstage.o cbfs-mkpayload.o # LZMA COMMON+=lzma/lzma.o diff --git a/util/cbfstool/Makefile.inc b/util/cbfstool/Makefile.inc index 2cee794572..e3eb7985fc 100644 --- a/util/cbfstool/Makefile.inc +++ b/util/cbfstool/Makefile.inc @@ -5,6 +5,7 @@ cbfsobj += compress.o cbfsobj += cbfs_image.o cbfsobj += cbfs-mkstage.o cbfsobj += cbfs-mkpayload.o +cbfsobj += fit.o # LZMA cbfsobj += lzma.o cbfsobj += LzFind.o diff --git a/util/cbfstool/cbfstool.c b/util/cbfstool/cbfstool.c index eeb6936510..1a3d25e44a 100644 --- a/util/cbfstool/cbfstool.c +++ b/util/cbfstool/cbfstool.c @@ -28,6 +28,7 @@ #include "common.h" #include "cbfs.h" #include "cbfs_image.h" +#include "fit.h" struct command { const char *name; @@ -53,6 +54,7 @@ static struct param { uint32_t pagesize; uint32_t offset; uint32_t top_aligned; + int fit_empty_entries; comp_algo algo; } param = { /* All variables not listed are initialized as zero. */ @@ -415,6 +417,36 @@ static int cbfs_extract(void) return result; } +static int cbfs_update_fit(void) +{ + int ret = 0; + struct cbfs_image image; + + if (!param.name) { + ERROR("You need to specify -n/--name.\n"); + return 1; + } + + if (param.fit_empty_entries <= 0) { + ERROR("Invalid number of fit entries " + "(-x/--empty-fits): %d\n", param.fit_empty_entries); + return 1; + } + + if (cbfs_image_from_file(&image, param.cbfs_name) != 0) { + ERROR("Could not load ROM image '%s'.\n", + param.cbfs_name); + return 1; + } + + ret = fit_update_table(&image, param.fit_empty_entries, param.name); + if (!ret) + ret = cbfs_image_write_file(&image, param.cbfs_name); + + cbfs_image_delete(&image); + return ret; +} + static const struct command commands[] = { {"add", "f:n:t:b:vh?", cbfs_add}, {"add-payload", "f:n:t:c:b:vh?", cbfs_add_payload}, @@ -425,6 +457,7 @@ static const struct command commands[] = { {"locate", "f:n:P:a:Tvh?", cbfs_locate}, {"print", "vh?", cbfs_print}, {"extract", "n:f:vh?", cbfs_extract}, + {"update-fit", "n:x:vh?", cbfs_update_fit}, }; static struct option long_options[] = { @@ -442,6 +475,7 @@ static struct option long_options[] = { {"offset", required_argument, 0, 'o' }, {"file", required_argument, 0, 'f' }, {"arch", required_argument, 0, 'm' }, + {"empty-fits", required_argument, 0, 'x' }, {"verbose", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {NULL, 0, 0, 0 } @@ -476,6 +510,8 @@ static void usage(char *name) "Show the contents of the ROM\n" " extract -n NAME -f FILE " "Extracts a raw payload from ROM\n" + " update-fit -n MICROCODE_BLOB_NAME -x EMTPY_FIT_ENTRIES\n " + "Updates the FIT table with microcode entries\n" "\n" "ARCHes:\n" " armv7, x86\n" @@ -586,6 +622,9 @@ int main(int argc, char **argv) case 'T': param.top_aligned = 1; break; + case 'x': + param.fit_empty_entries = strtol(optarg, NULL, 0); + break; case 'v': verbose++; break; diff --git a/util/cbfstool/fit.c b/util/cbfstool/fit.c new file mode 100644 index 0000000000..12a7e3b49f --- /dev/null +++ b/util/cbfstool/fit.c @@ -0,0 +1,277 @@ +/* + * Firmware Interface Table support. + * + * Copyright (C) 2012 Google Inc. + * + * 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 <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include "common.h" +#include "cbfs.h" +#include "cbfs_image.h" +#include "fit.h" + +/* FIXME: This code assumes it is being executed on a little endian machine. */ + +#define FIT_POINTER_LOCATION 0xffffffc0 +#define FIT_TABLE_LOWEST_ADDRESS ((uint32_t)(-(16 << 20))) +#define FIT_ENTRY_CHECKSUM_VALID 0x80 +#define FIT_TYPE_HEADER 0x0 +#define FIT_HEADER_VERSION 0x0100 +#define FIT_HEADER_ADDRESS "_FIT_ " +#define FIT_TYPE_MICROCODE 0x1 +#define FIT_MICROCODE_VERSION 0x0100 + +struct fit_entry { + uint64_t address; + uint32_t size_reserved; + uint16_t version; + uint8_t type_checksum_valid; + uint8_t checksum; +} __attribute__ ((packed)); + +struct fit_table { + struct fit_entry header; + struct fit_entry entries[0]; +} __attribute__ ((packed)); + +struct microcode_header { + uint32_t version; + uint32_t revision; + uint32_t date; + uint32_t processor_signature; + uint32_t checksum; + uint32_t loader_revision; + uint32_t processor_flags; + uint32_t data_size; + uint32_t total_size; + uint8_t reserved[12]; +} __attribute__ ((packed)); + +struct microcode_entry { + int offset; + int size; +}; + +static inline void *rom_buffer_pointer(struct cbfs_image *image, int offset) +{ + return &image->buffer.data[offset]; +} + +static inline int fit_entry_size_bytes(struct fit_entry *entry) +{ + return (entry->size_reserved & 0xffffff) << 4; +} + +static inline void fit_entry_update_size(struct fit_entry *entry, + int size_bytes) +{ + /* Size is multiples of 16 bytes. */ + entry->size_reserved = (size_bytes >> 4) & 0xffffff; +} + +static inline void fit_entry_add_size(struct fit_entry *entry, + int size_bytes) +{ + int size = fit_entry_size_bytes(entry); + size += size_bytes; + fit_entry_update_size(entry, size); +} + +static inline int fit_entry_type(struct fit_entry *entry) +{ + return entry->type_checksum_valid & ~FIT_ENTRY_CHECKSUM_VALID; +} + +/* + * Get an offset from a host pointer. This function assumes the ROM is located + * in the host address space at [4G - romsize -> 4G). It also assume all + * pointers have values within this address range. + */ +static inline int ptr_to_offset(uint32_t romsize, uint32_t host_ptr) +{ + return (int)(romsize + host_ptr); +} + +/* + * Get a pointer from an offset. This function assumes the ROM is located + * in the host address space at [4G - romsize -> 4G). It also assume all + * pointers have values within this address range. + */ +static inline uint32_t offset_to_ptr(uint32_t romsize, int offset) +{ + return -(romsize - (uint32_t )offset); +} + +static struct fit_table *locate_fit_table(struct cbfs_image *image) +{ + struct fit_table *table; + uint32_t *fit_pointer; + + fit_pointer = rom_buffer_pointer(image, + ptr_to_offset(image->buffer.size, FIT_POINTER_LOCATION)); + + /* Ensure pointer is below 4GiB and within 16MiB of 4GiB */ + if (fit_pointer[1] != 0 || fit_pointer[0] < FIT_TABLE_LOWEST_ADDRESS) + return NULL; + + table = rom_buffer_pointer(image, + ptr_to_offset(image->buffer.size, *fit_pointer)); + + /* Check that the address field has the proper signature. */ + if (strncmp((const char *)&table->header.address, FIT_HEADER_ADDRESS, + sizeof(table->header.address))) + return NULL; + + if (table->header.version != FIT_HEADER_VERSION) + return NULL; + + if (fit_entry_type(&table->header) != FIT_TYPE_HEADER) + return NULL; + + /* Assume that the FIT table only contains the header */ + if (fit_entry_size_bytes(&table->header) != sizeof(struct fit_entry)) + return NULL; + + return table; +} + +static void update_fit_checksum(struct fit_table *fit) +{ + int size_bytes; + uint8_t *buffer; + uint8_t result; + int i; + + fit->header.checksum = 0; + size_bytes = fit_entry_size_bytes(&fit->header); + result = 0; + buffer = (void *)fit; + for (i = 0; i < size_bytes; i++) + result += buffer[i]; + fit->header.checksum = -result; +} + +static void add_microcodde_entries(struct cbfs_image *image, + struct fit_table *fit, + struct microcode_entry *mcus, int num_mcus) +{ + int i; + + for (i = 0; i < num_mcus; i++) { + struct fit_entry *entry = &fit->entries[i]; + struct microcode_entry *mcu = &mcus[i]; + + entry->address = offset_to_ptr(image->buffer.size, mcu->offset); + fit_entry_update_size(entry, mcu->size); + entry->version = FIT_MICROCODE_VERSION; + entry->type_checksum_valid = FIT_TYPE_MICROCODE; + entry->checksum = 0; + fit_entry_add_size(&fit->header, sizeof(struct fit_entry)); + } +} + +static int parse_microcode_blob(struct cbfs_image *image, + struct cbfs_file *mcode_file, + struct microcode_entry *mcus, int *total_mcus) +{ + int num_mcus; + int current_offset; + int file_length; + + current_offset = (int)((char *)mcode_file - image->buffer.data); + current_offset += ntohl(mcode_file->offset); + file_length = ntohl(mcode_file->len); + + num_mcus = 0; + while (file_length > sizeof(struct microcode_header)) + { + struct microcode_header *mcu_header; + + mcu_header = rom_buffer_pointer(image, current_offset); + + /* FIXME: Should the checksum be validated? */ + mcus[num_mcus].offset = current_offset; + mcus[num_mcus].size = mcu_header->total_size; + + /* Proceed to next payload. */ + current_offset += mcus[num_mcus].size; + num_mcus++; + file_length -= mcus[num_mcus].size; + + /* Can't determine any more entries. */ + if (!mcu_header->total_size) + break; + + /* Reached limit of FIT entries. */ + if (num_mcus == *total_mcus) + break; + if (file_length < sizeof(struct microcode_header)) + break; + } + + /* Update how many microcode updates we found. */ + *total_mcus = num_mcus; + + return 0; +} + +int fit_update_table(struct cbfs_image *image, int empty_entries, + const char *microcode_blob_name) +{ + struct fit_table *fit; + struct cbfs_file *mcode_file; + struct microcode_entry *mcus; + int ret = 0; + // struct rom_image image = { .rom = rom, .size = romsize, }; + + fit = locate_fit_table(image); + + if (!fit) { + ERROR("FIT not found.\n"); + return 1; + } + + mcode_file = cbfs_get_entry(image, microcode_blob_name); + if (!mcode_file) { + ERROR("File '%s' not found in CBFS.\n", + microcode_blob_name); + return 1; + } + + mcus = malloc(sizeof(*mcus) * empty_entries); + + if (!mcus) { + ERROR("Couldn't allocate memory for microcode update entries.\n"); + return 1; + } + + if (parse_microcode_blob(image, mcode_file, mcus, &empty_entries)) { + ERROR("Couldn't parse microcode blob.\n"); + ret = 1; + goto out; + } + + add_microcodde_entries(image, fit, mcus, empty_entries); + update_fit_checksum(fit); + +out: + free(mcus); + return ret; +} diff --git a/util/cbfstool/fit.h b/util/cbfstool/fit.h new file mode 100644 index 0000000000..63e22f4a2b --- /dev/null +++ b/util/cbfstool/fit.h @@ -0,0 +1,25 @@ +/* + * Firmware Interface Table support. + * + * Copyright (C) 2012 Google Inc. + * + * 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 + */ + +#ifndef __CBFSTOOL_FIT_H +#define __CBFSTOOL_FIT_H + +int fit_update_table(struct cbfs_image *image, + int empty_entries, const char *microcode_blob_name); +#endif |