Index: src/platform/vboot_reference/utility/gbb_utility.cc |
diff --git a/src/platform/vboot_reference/utility/gbb_utility.cc b/src/platform/vboot_reference/utility/gbb_utility.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b0aab56cb7a32836a0bb26a7d8fd1fe8e132ce64 |
--- /dev/null |
+++ b/src/platform/vboot_reference/utility/gbb_utility.cc |
@@ -0,0 +1,533 @@ |
+// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+// |
+// Utility for manipulating Google Binary Block (GBB) |
+// |
+ |
+#include "gbb_utility.h" |
+ |
+#include <assert.h> |
+#include <getopt.h> |
+#include <stdio.h> |
+#include <string.h> |
+ |
+#include <string> |
+#include <vector> |
+#include <algorithm> |
+ |
+using std::string; |
+ |
+/////////////////////////////////////////////////////////////////////// |
+// Simple File Utilities |
+ |
+// utility function: read a non-empty file. |
+// return file content, or empty for any failure. |
+static string read_nonempty_file(const char *filename) { |
+ string file_content; |
+ std::vector<char> buffer; // since image files are small, should be OK |
+ |
+ FILE *fp = fopen(filename, "rb"); |
+ if (!fp) { |
+ perror(filename); |
+ return file_content; |
+ } |
+ |
+ // prepare buffer on successful seek |
+ if (fseek(fp, 0, SEEK_END) == 0) { |
+ buffer.resize(ftell(fp)); |
+ rewind(fp); |
+ } |
+ |
+ if (!buffer.empty()) { |
+ if (fread(&buffer[0], buffer.size(), 1, fp) != 1) { |
+ perror(filename); |
+ buffer.clear(); // discard buffer when read fail. |
+ } else { |
+ file_content.assign(buffer.begin(), buffer.end()); |
+ } |
+ } |
+ |
+ fclose(fp); |
+ return file_content; |
+} |
+ |
+// utility function: write non-empty content to file. |
+// return true on success, otherwise false. |
+static bool write_nonempty_file(const char *filename, const string &content) { |
+ assert(!content.empty()); |
+ |
+ FILE *fp = fopen(filename, "wb"); |
+ if (!fp) { |
+ perror(filename); |
+ return false; |
+ } |
+ |
+ int r = fwrite(content.c_str(), content.size(), 1, fp); |
+ fclose(fp); |
+ |
+ if (r != 1) |
+ perror(filename); |
+ |
+ return r == 1; |
+} |
+ |
+/////////////////////////////////////////////////////////////////////// |
+// GBB Utility implementation |
+ |
+namespace vboot_reference { |
+ |
+GoogleBinaryBlockUtil::GoogleBinaryBlockUtil() { |
+ assert(sizeof(header_) == GBB_HEADER_SIZE); |
+ initialize(); |
+} |
+ |
+GoogleBinaryBlockUtil::~GoogleBinaryBlockUtil() { |
+} |
+ |
+void GoogleBinaryBlockUtil::initialize() { |
+ verbose = true; |
+ is_valid_gbb = false; |
+ header_offset_ = 0; |
+ memset(&header_, 0, sizeof(header_)); |
+ file_content_.clear(); |
+} |
+ |
+bool GoogleBinaryBlockUtil::load_from_file(const char *filename) { |
+ is_valid_gbb = false; |
+ |
+ file_content_ = read_nonempty_file(filename); |
+ if (file_content_.empty()) |
+ return false; |
+ |
+ switch (search_header_signatures(file_content_, &header_offset_)) { |
+ case 0: |
+ if (verbose) |
+ fprintf(stderr, " error: cannot find any GBB signature.\n"); |
+ break; |
+ |
+ case 1: |
+ // fetch a copy of block header to check more detail |
+ if (!load_gbb_header(file_content_, header_offset_, &header_)) { |
+ if (verbose) |
+ fprintf(stderr, " error: invalid GBB in image file.\n"); |
+ } else { |
+ is_valid_gbb = true; |
+ } |
+ break; |
+ |
+ default: |
+ if (verbose) |
+ fprintf(stderr, " error: found multiple GBB signatures.\n"); |
+ file_content_.clear(); |
+ break; |
+ } |
+ |
+ // discard if anything goes wrong |
+ if (!is_valid_gbb) |
+ initialize(); |
+ |
+ return is_valid_gbb; |
+} |
+ |
+bool GoogleBinaryBlockUtil::save_to_file(const char *filename) { |
+ assert(is_valid_gbb && !file_content_.empty()); |
+ return write_nonempty_file(filename, file_content_); |
+} |
+ |
+int GoogleBinaryBlockUtil::search_header_signatures(const string &image, |
+ long *poffset) const { |
+ int found_signatures = 0; |
+ size_t last_found_pos = 0; |
+ |
+ while ((last_found_pos = |
+ file_content_.find(GBB_SIGNATURE, last_found_pos, GBB_SIGNATURE_SIZE)) |
+ != file_content_.npos) { |
+ *poffset = last_found_pos; |
+ found_signatures++; |
+ last_found_pos++; // for next iteration |
+ } |
+ |
+ return found_signatures; |
+} |
+ |
+// uility function for load_gbb_header to check property range |
+static bool check_property_range(uint32_t off, uint32_t sz, |
+ uint32_t hdr_sz, uint32_t max_sz, |
+ const char *prop_name, bool verbose) { |
+ if (off + sz > max_sz) { |
+ if (verbose) |
+ fprintf(stderr, " error: property %s exceed GBB.\n", prop_name); |
+ return false; |
+ } |
+ |
+ if (off < hdr_sz) { |
+ if (verbose) |
+ fprintf(stderr, " error: property %s overlap GBB header.\n", prop_name); |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool GoogleBinaryBlockUtil::load_gbb_header(const string &image, long offset, |
+ GoogleBinaryBlockHeader *phdr) const { |
+ assert(phdr); |
+ |
+ // check that GBB header does not extend past end of image |
+ if (image.size() < (size_t)offset + GBB_HEADER_SIZE) { |
+ if (verbose) |
+ fprintf(stderr, " error: incomplete GBB.\n"); |
+ return false; |
+ } |
+ |
+ string::const_iterator block_ptr = image.begin() + offset; |
+ size_t block_size = image.size() - offset; |
+ |
+ std::copy(block_ptr, block_ptr + GBB_HEADER_SIZE, |
+ reinterpret_cast<char*>(phdr)); |
+ |
+ const GoogleBinaryBlockHeader &h = *phdr; // for quick access |
+ |
+ // check version |
+ if (h.major_version != GBB_MAJOR_VER || |
+ h.minor_version != GBB_MINOR_VER) { |
+ if (verbose) |
+ fprintf(stderr, " error: invalid GBB version (%d.%d)\n", |
+ h.major_version, h.minor_version); |
+ return false; |
+ } |
+ |
+ if (h.header_size < GBB_HEADER_SIZE) { |
+ if (verbose) |
+ fprintf(stderr, " error: incompatible header size (%d < %d)\n", |
+ h.header_size, GBB_HEADER_SIZE); |
+ return false; |
+ } |
+ |
+ // verify location of properties |
+ if (!check_property_range(h.hwid_offset, h.hwid_size, |
+ h.header_size, block_size, "hwid", verbose) || |
+ !check_property_range(h.rootkey_offset, h.rootkey_size, |
+ h.header_size, block_size, "rootkey", verbose) || |
+ !check_property_range(h.bmpfv_offset, h.bmpfv_size, |
+ h.header_size, block_size, "bmpfv", verbose)) { |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool GoogleBinaryBlockUtil::find_property(PROPINDEX i, |
+ uint32_t *poffset, |
+ uint32_t *psize) const { |
+ switch (i) { |
+ case PROP_HWID: |
+ *poffset = header_.hwid_offset; |
+ *psize = header_.hwid_size; |
+ break; |
+ |
+ case PROP_ROOTKEY: |
+ *poffset = header_.rootkey_offset; |
+ *psize = header_.rootkey_size; |
+ break; |
+ |
+ case PROP_BMPFV: |
+ *poffset = header_.bmpfv_offset; |
+ *psize = header_.bmpfv_size; |
+ break; |
+ |
+ default: |
+ assert(!"invalid property index."); |
+ return false; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool GoogleBinaryBlockUtil::set_property(PROPINDEX i, const string &value) { |
+ uint32_t prop_size; |
+ uint32_t prop_offset; |
+ |
+ assert(is_valid_gbb); |
+ |
+ if (!find_property(i, &prop_offset, &prop_size)) { |
+ if (verbose) |
+ fprintf(stderr, " internal error: unknown property (%d).\n", |
+ static_cast<int>(i)); |
+ return false; |
+ } |
+ |
+ if (prop_size < value.size()) { |
+ if (verbose) |
+ fprintf(stderr, " error: value size (%zu) exceed capacity (%u).\n", |
+ value.size(), prop_size); |
+ return false; |
+ } |
+ |
+ if (i == PROP_HWID && prop_size == value.size()) { |
+ // special case: this is NUL-terminated so it's better to keep one more \0 |
+ if (verbose) |
+ fprintf(stderr, "error: NUL-terminated string exceed capacity (%d)\n", |
+ prop_size); |
+ return false; |
+ } |
+ |
+ string::iterator dest = file_content_.begin() + header_offset_ + prop_offset; |
+ file_content_.replace(dest, dest+prop_size, prop_size, '\0'); // wipe first |
+ std::copy(value.begin(), value.end(), dest); |
+ |
+ return true; |
+} |
+ |
+string GoogleBinaryBlockUtil::get_property(PROPINDEX i) const { |
+ uint32_t prop_size; |
+ uint32_t prop_offset; |
+ |
+ assert(is_valid_gbb); |
+ |
+ if (!find_property(i, &prop_offset, &prop_size)) { |
+ if (verbose) |
+ fprintf(stderr, " internal error: unknown property (%d).\n", |
+ static_cast<int>(i)); |
+ return ""; |
+ } |
+ |
+ string::const_iterator dest = file_content_.begin() + |
+ header_offset_ + prop_offset; |
+ return string(dest, dest + prop_size); |
+} |
+ |
+bool GoogleBinaryBlockUtil::set_hwid(const char *hwid) { |
+ return set_property(PROP_HWID, hwid); |
+} |
+ |
+bool GoogleBinaryBlockUtil::set_rootkey(const std::string &value) { |
+ return set_property(PROP_ROOTKEY, value); |
+} |
+ |
+bool GoogleBinaryBlockUtil::set_bmpfv(const string &value) { |
+ return set_property(PROP_BMPFV, value); |
+} |
+ |
+} // namespace vboot_reference |
+ |
+#ifdef WITH_UTIL_MAIN |
+ |
+/////////////////////////////////////////////////////////////////////// |
+// command line utilities |
+ |
+// utility function: provide usage of this utility and exit. |
+static void usagehelp_exit(const char *prog_name) { |
+ printf( |
+ "Utility to manage Google Binary Block (GBB)\n" |
+ "Usage: %s [-g|-s] [OPTIONS] bios_file [output_file]\n\n" |
+ "-g, --get \tGet (read) from bios_file, " |
+ "with following options:\n" |
+ " --hwid \tReport hardware id (default).\n" |
+ " -k, --rootkey=FILE \tFile name to export Root Key.\n" |
+ " -b, --bmpfv=FILE \tFile name to export Bitmap FV.\n" |
+ "\n" |
+ "-s, --set \tSet (write) to bios_file, " |
+ "with following options:\n" |
+ " -i, --hwid=HWID \tThe new hardware id to be changed.\n" |
+ " -k, --rootkey=FILE \tFile name of new Root Key.\n" |
+ " -b, --bmpfv=FILE \tFile name of new Bitmap FV\n" |
+ "\n" |
+ " SAMPLE:\n" |
+ " %s -g bios.bin\n" |
+ " %s --set --hwid='New Model' -k key.bin bios.bin newbios.bin\n" |
+ , prog_name, prog_name, prog_name); |
+ exit(1); |
+} |
+ |
+// utility function: export a property from GBB to given file. |
+// return true on success, otherwise false. |
+static bool export_property_to_file(const string &filename, |
+ const char *name, const string &value) { |
+ assert(!filename.empty()); |
+ const char *fn = filename.c_str(); |
+ |
+ if (!write_nonempty_file(fn, value)) { |
+ fprintf(stderr, "error: failed to export %s to file: %s\n", name, fn); |
+ return false; |
+ } |
+ |
+ printf(" - exported %s to file: %s\n", name, fn); |
+ return true; |
+} |
+ |
+// utility function: import a property to GBB by given file. |
+// return true on success, otherwise false. |
+// is succesfully imported into GBB. |
+static bool import_property_from_file( |
+ const string &filename, const char *name, |
+ bool (vboot_reference::GoogleBinaryBlockUtil::*setter)(const string &value), |
+ vboot_reference::GoogleBinaryBlockUtil *putil) { |
+ assert(!filename.empty()); |
+ |
+ printf(" - import %s from %s: ", name, filename.c_str()); |
+ string v = read_nonempty_file(filename.c_str()); |
+ if (v.empty()) { |
+ printf("invalid file.\n"); |
+ return false; |
+ } |
+ |
+ if (!(putil->*setter)(v)) { |
+ printf("invalid content.\n"); |
+ return false; |
+ } |
+ |
+ printf("success.\n"); |
+ return true; |
+} |
+ |
+/////////////////////////////////////////////////////////////////////// |
+// main |
+ |
+int main(int argc, char *argv[]) { |
+ const char *myname = argv[0]; |
+ int err_stage = 0; // an indicator for error exits |
+ |
+ struct GBBUtilOptions { |
+ bool get_mode, set_mode; |
+ bool use_hwid, use_rootkey, use_bmpfv; |
+ string hwid, rootkey_fn, bmpfv_fn; |
+ } myopts; |
+ |
+ myopts.get_mode = myopts.set_mode = false; |
+ myopts.use_hwid = myopts.use_rootkey = myopts.use_bmpfv = false; |
+ |
+ // snippets for getopt_long |
+ int option_index, opt; |
+ static struct option long_options[] = { |
+ {"get", 0, NULL, 'g' }, |
+ {"set", 0, NULL, 's' }, |
+ {"hwid", 2, NULL, 'i' }, |
+ {"rootkey", 1, NULL, 'k' }, |
+ {"bmpfv", 1, NULL, 'b' }, |
+ { NULL, 0, NULL, 0 }, |
+ }; |
+ int opt_props = 0; // number of assigned properties. |
+ |
+ // parse command line options |
+ while ((opt = getopt_long(argc, argv, "gsi:k:b:", |
+ long_options, &option_index)) >= 0) { |
+ switch (opt) { |
+ case 'g': |
+ myopts.get_mode = true; |
+ break; |
+ |
+ case 's': |
+ myopts.set_mode = true; |
+ break; |
+ |
+ case 'i': |
+ opt_props++; |
+ myopts.use_hwid = true; |
+ if (optarg) |
+ myopts.hwid = optarg; |
+ break; |
+ |
+ case 'k': |
+ opt_props++; |
+ myopts.use_rootkey = true; |
+ myopts.rootkey_fn = optarg; |
+ break; |
+ |
+ case 'b': |
+ opt_props++; |
+ myopts.use_bmpfv = true; |
+ myopts.bmpfv_fn = optarg; |
+ break; |
+ |
+ default: |
+ case '?': |
+ usagehelp_exit(myname); |
+ break; |
+ } |
+ } |
+ argc -= optind; |
+ argv += optind; |
+ |
+ // check parameters configuration |
+ if (!(argc == 1 || (myopts.set_mode && argc == 2))) |
+ usagehelp_exit(myname); |
+ |
+ // stage: parameter parsing |
+ err_stage++; |
+ if (myopts.get_mode == myopts.set_mode) { |
+ printf("error: please assign either get or set mode.\n"); |
Randall Spangler
2010/05/28 16:57:34
Could assume that get is the default, and then onl
|
+ return err_stage; |
+ } |
+ |
+ // stage: load image files |
+ err_stage++; |
+ vboot_reference::GoogleBinaryBlockUtil util; |
+ const char *input_filename = argv[0], |
+ *output_filename= (argc > 1) ? argv[1] : argv[0]; |
+ |
+ if (!util.load_from_file(input_filename)) { |
+ printf("error: cannot load valid BIOS file: %s\n", input_filename); |
+ return err_stage; |
+ } |
+ |
+ // stage: processing by mode |
+ err_stage++; |
+ if (myopts.get_mode) { |
+ // get mode |
+ if (opt_props < 1) // enable hwid by default |
+ myopts.use_hwid = true; |
+ |
+ if (myopts.use_hwid) |
+ printf("Hardware ID: %s\n", util.get_hwid().c_str()); |
+ if (myopts.use_rootkey) |
+ export_property_to_file(myopts.rootkey_fn, "rootkey", util.get_rootkey()); |
+ if (myopts.use_bmpfv) |
+ export_property_to_file(myopts.bmpfv_fn, "bmpfv", util.get_bmpfv()); |
+ } else { |
+ // set mode |
+ assert(myopts.set_mode); |
+ if (opt_props < 1) { |
+ printf("nothing to change. abort.\n"); |
+ return err_stage; |
+ } |
+ |
+ // HWID does not come from file, so update it direcly here. |
+ if (myopts.use_hwid) { |
+ string old_hwid = util.get_hwid(); |
+ if (!util.set_hwid(myopts.hwid.c_str())) { |
+ printf("error: inproper hardware id: %s\n", |
+ myopts.hwid.c_str()); |
+ return err_stage; |
+ } |
+ printf(" - Hardware id changed: %s -> %s.\n", |
+ old_hwid.c_str(), util.get_hwid().c_str()); |
+ } |
+ |
+ // import other properties from file |
+ if ((myopts.use_rootkey && |
+ !import_property_from_file(myopts.rootkey_fn, "rootkey", |
+ &vboot_reference::GoogleBinaryBlockUtil::set_rootkey, &util)) || |
+ (myopts.use_bmpfv && |
+ !import_property_from_file(myopts.bmpfv_fn, "bmpfv", |
+ &vboot_reference::GoogleBinaryBlockUtil::set_bmpfv, &util))) { |
+ printf("error: cannot set new properties. abort.\n"); |
+ return err_stage; |
+ } |
+ |
+ // stage: write output |
+ err_stage++; |
+ if (!util.save_to_file(output_filename)) { |
+ printf("error: cannot save to file: %s\n", output_filename); |
+ return err_stage; |
+ } else { |
+ printf("successfully saved new image to: %s\n", output_filename); |
+ } |
+ } |
+ |
+ return 0; |
+} |
+ |
+#endif // WITH_UTIL_MAIN |
+ |