Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1427)

Unified Diff: src/platform/vboot_reference/utility/gbb_utility.cc

Issue 2346001: Added gbb_utility (tool for Google Binary Block) (Closed) Base URL: ssh://gitrw.chromium.org/chromiumos
Patch Set: updated gbb utility Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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
+
« no previous file with comments | « src/platform/vboot_reference/utility/Makefile ('k') | src/platform/vboot_reference/utility/include/gbb_utility.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698