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 |
index b0aab56cb7a32836a0bb26a7d8fd1fe8e132ce64..8b10787b4a96e20fe0dd2d71121a95105540aa5e 100644 |
--- a/src/platform/vboot_reference/utility/gbb_utility.cc |
+++ b/src/platform/vboot_reference/utility/gbb_utility.cc |
@@ -155,6 +155,13 @@ int GoogleBinaryBlockUtil::search_header_signatures(const string &image, |
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) { |
+ // for backward compatibility, we allow zero entry here. |
+ if (off == 0 && sz == 0) { |
+ if (verbose) |
+ fprintf(stderr, " warning: property %s is EMPTY.\n", prop_name); |
+ return true; |
+ } |
+ |
if (off + sz > max_sz) { |
if (verbose) |
fprintf(stderr, " error: property %s exceed GBB.\n", prop_name); |
@@ -205,14 +212,20 @@ bool GoogleBinaryBlockUtil::load_gbb_header(const string &image, long offset, |
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; |
+ // verify properties |
+ for (int i = 0; i < PROP_RANGE; i++) { |
+ uint32_t off, size; |
+ const char *name; |
+ |
+ if (!find_property(static_cast<PROPINDEX>(i), |
+ &off, &size, &name)) { |
+ assert(!"invalid property."); |
+ return false; |
+ } |
+ |
+ if (!check_property_range(off, size, |
+ h.header_size, block_size, name, verbose)) |
+ return false; |
} |
return true; |
@@ -220,21 +233,28 @@ bool GoogleBinaryBlockUtil::load_gbb_header(const string &image, long offset, |
bool GoogleBinaryBlockUtil::find_property(PROPINDEX i, |
uint32_t *poffset, |
- uint32_t *psize) const { |
+ uint32_t *psize, |
+ const char** pname) const { |
switch (i) { |
case PROP_HWID: |
*poffset = header_.hwid_offset; |
*psize = header_.hwid_size; |
+ if (pname) |
+ *pname = "hardware_id"; |
break; |
case PROP_ROOTKEY: |
*poffset = header_.rootkey_offset; |
*psize = header_.rootkey_size; |
+ if (pname) |
+ *pname = "root_key"; |
break; |
case PROP_BMPFV: |
*poffset = header_.bmpfv_offset; |
*psize = header_.bmpfv_size; |
+ if (pname) |
+ *pname = "bmp_fv"; |
break; |
default: |
@@ -248,10 +268,11 @@ bool GoogleBinaryBlockUtil::find_property(PROPINDEX i, |
bool GoogleBinaryBlockUtil::set_property(PROPINDEX i, const string &value) { |
uint32_t prop_size; |
uint32_t prop_offset; |
+ const char *prop_name; |
assert(is_valid_gbb); |
- if (!find_property(i, &prop_offset, &prop_size)) { |
+ if (!find_property(i, &prop_offset, &prop_size, &prop_name)) { |
if (verbose) |
fprintf(stderr, " internal error: unknown property (%d).\n", |
static_cast<int>(i)); |
@@ -260,16 +281,16 @@ bool GoogleBinaryBlockUtil::set_property(PROPINDEX i, const string &value) { |
if (prop_size < value.size()) { |
if (verbose) |
- fprintf(stderr, " error: value size (%zu) exceed capacity (%u).\n", |
- value.size(), prop_size); |
+ fprintf(stderr, " error: value size (%zu) exceed property capacity " |
+ "(%u): %s\n", value.size(), prop_size, prop_name); |
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); |
+ fprintf(stderr, "error: NUL-terminated string exceed capacity (%d): %s\n", |
+ prop_size, prop_name); |
return false; |
} |
@@ -283,21 +304,45 @@ bool GoogleBinaryBlockUtil::set_property(PROPINDEX i, const string &value) { |
string GoogleBinaryBlockUtil::get_property(PROPINDEX i) const { |
uint32_t prop_size; |
uint32_t prop_offset; |
+ const char *prop_name; |
assert(is_valid_gbb); |
- if (!find_property(i, &prop_offset, &prop_size)) { |
+ if (!find_property(i, &prop_offset, &prop_size, &prop_name)) { |
if (verbose) |
fprintf(stderr, " internal error: unknown property (%d).\n", |
static_cast<int>(i)); |
return ""; |
} |
+ // check range again to allow empty value (for compatbility) |
+ if (prop_offset == 0 && prop_size == 0) { |
+ if (verbose) |
+ fprintf(stderr, " warning: empty property (%d): %s.\n", |
+ static_cast<int>(i), prop_name); |
+ return ""; |
+ } |
+ |
string::const_iterator dest = file_content_.begin() + |
header_offset_ + prop_offset; |
return string(dest, dest + prop_size); |
} |
+string GoogleBinaryBlockUtil::get_property_name(PROPINDEX i) const { |
+ uint32_t unused_off, unused_size; |
+ const char *prop_name; |
+ |
+ if (!find_property(i, &unused_off, &unused_size, &prop_name)) { |
+ if (verbose) |
+ fprintf(stderr, " internal error: unknown property (%d).\n", |
+ static_cast<int>(i)); |
+ assert(!"invalid property index."); |
+ return ""; |
+ } |
+ |
+ return prop_name; |
+} |
+ |
bool GoogleBinaryBlockUtil::set_hwid(const char *hwid) { |
return set_property(PROP_HWID, hwid); |
} |
@@ -310,31 +355,39 @@ bool GoogleBinaryBlockUtil::set_bmpfv(const string &value) { |
return set_property(PROP_BMPFV, value); |
} |
-} // namespace vboot_reference |
+} // namespace vboot_reference |
#ifdef WITH_UTIL_MAIN |
/////////////////////////////////////////////////////////////////////// |
// command line utilities |
+#include <map> |
+ |
+using vboot_reference::GoogleBinaryBlockUtil; |
+ |
// 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, " |
+ "Usage: %s [-g|-s] [OPTIONS] bios_file [output_file]\n" |
+ "\n" |
+ "GET MODE:\n" |
+ "-g, --get (default)\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" |
+ "SET MODE:\n" |
"-s, --set \tSet (write) to bios_file, " |
"with following options:\n" |
+ " -o, --output=FILE \tNew file name for ouptput.\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" |
+ " -b, --bmpfv=FILE \tFile name of new Bitmap FV.\n" |
"\n" |
- " SAMPLE:\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); |
@@ -342,43 +395,63 @@ static void usagehelp_exit(const char *prog_name) { |
} |
// utility function: export a property from GBB to given file. |
+// if filename was empty, export to console (screen). |
// 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(); |
+static bool export_property(GoogleBinaryBlockUtil::PROPINDEX idx, |
+ const string &filename, |
+ const GoogleBinaryBlockUtil &util) { |
+ string prop_name = util.get_property_name(idx), |
+ value = util.get_property(idx); |
+ const char *name = prop_name.c_str(); |
+ |
+ if (filename.empty()) { |
+ // write to console |
+ printf("%s: %s\n", name, value.c_str()); |
+ } else { |
+ 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; |
+ 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); |
} |
- printf(" - exported %s to file: %s\n", name, fn); |
return true; |
} |
-// utility function: import a property to GBB by given file. |
+// utility function: import a property to GBB by given source (file or string). |
// 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; |
+static bool import_property( |
+ GoogleBinaryBlockUtil::PROPINDEX idx, const string &source, |
+ bool source_as_file, GoogleBinaryBlockUtil *putil) { |
+ assert(!source.empty()); |
+ string prop_name = putil->get_property_name(idx); |
+ |
+ if (source_as_file) { |
+ printf(" - import %s from %s: ", prop_name.c_str(), source.c_str()); |
+ string v = read_nonempty_file(source.c_str()); |
+ if (v.empty()) { |
+ printf("invalid file.\n"); |
+ return false; |
+ } |
+ if (!putil->set_property(idx, v)) { |
+ printf("invalid content.\n"); |
+ return false; |
+ } |
+ printf("success.\n"); |
+ } else { |
+ // source as string |
+ string old_value = putil->get_property(idx); |
+ bool result = putil->set_property(idx, source); |
+ printf(" - %s changed from '%s' to '%s': %s\n", |
+ prop_name.c_str(), old_value.c_str(), source.c_str(), |
+ result ? "success" : "failed"); |
+ if (!result) |
+ return false; |
} |
- printf("success.\n"); |
return true; |
} |
@@ -389,29 +462,39 @@ int main(int argc, char *argv[]) { |
const char *myname = argv[0]; |
int err_stage = 0; // an indicator for error exits |
+ // small parameter helper class |
+ class OptPropertyMap: public |
+ std::map<GoogleBinaryBlockUtil::PROPINDEX, string> { |
+ public: |
+ bool set_new_value(GoogleBinaryBlockUtil::PROPINDEX id, const string &v) { |
+ if (find(id) != end()) |
+ return false; |
+ (*this)[id] = v; |
+ return true; |
+ } |
+ }; |
+ OptPropertyMap opt_props; |
+ |
struct GBBUtilOptions { |
bool get_mode, set_mode; |
- bool use_hwid, use_rootkey, use_bmpfv; |
- string hwid, rootkey_fn, bmpfv_fn; |
+ string input_fn, output_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' }, |
+ {"get", 0, NULL, 'g' }, |
+ {"set", 0, NULL, 's' }, |
+ {"output", 1, NULL, 'o' }, |
+ {"hwid", 2, NULL, 'i' }, |
{"rootkey", 1, NULL, 'k' }, |
- {"bmpfv", 1, NULL, 'b' }, |
- { NULL, 0, NULL, 0 }, |
+ {"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:", |
+ while ((opt = getopt_long(argc, argv, "gso:i:k:b:", |
long_options, &option_index)) >= 0) { |
switch (opt) { |
case 'g': |
@@ -422,23 +505,26 @@ int main(int argc, char *argv[]) { |
myopts.set_mode = true; |
break; |
+ case 'o': |
+ myopts.output_fn = optarg; |
+ break; |
+ |
case 'i': |
- opt_props++; |
- myopts.use_hwid = true; |
- if (optarg) |
- myopts.hwid = optarg; |
+ if (!opt_props.set_new_value( |
+ GoogleBinaryBlockUtil::PROP_HWID, optarg ? optarg : "")) |
+ usagehelp_exit(myname); |
break; |
case 'k': |
- opt_props++; |
- myopts.use_rootkey = true; |
- myopts.rootkey_fn = optarg; |
+ if (!opt_props.set_new_value( |
+ GoogleBinaryBlockUtil::PROP_ROOTKEY, optarg)) |
+ usagehelp_exit(myname); |
break; |
case 'b': |
- opt_props++; |
- myopts.use_bmpfv = true; |
- myopts.bmpfv_fn = optarg; |
+ if (!opt_props.set_new_value( |
+ GoogleBinaryBlockUtil::PROP_BMPFV, optarg)) |
+ usagehelp_exit(myname); |
break; |
default: |
@@ -450,25 +536,42 @@ int main(int argc, char *argv[]) { |
argc -= optind; |
argv += optind; |
- // check parameters configuration |
- if (!(argc == 1 || (myopts.set_mode && argc == 2))) |
+ // adjust non-dashed parameters |
+ if (myopts.output_fn.empty() && argc == 2) { |
+ myopts.output_fn = argv[1]; |
+ argc--; |
+ } |
+ |
+ // currently, the only parameter is 'input file'. |
+ if (argc == 1) { |
+ myopts.input_fn = argv[0]; |
+ } else { |
usagehelp_exit(myname); |
+ } |
- // stage: parameter parsing |
+ // stage: complete parameter parsing and checking |
err_stage++; |
if (myopts.get_mode == myopts.set_mode) { |
- printf("error: please assign either get or set mode.\n"); |
+ if (myopts.get_mode) { |
+ printf("error: please assign either get or set mode.\n"); |
+ return err_stage; |
+ } else { |
+ // enter 'get' mode by default, if not assigned. |
+ myopts.get_mode = true; |
+ } |
+ } |
+ if (myopts.get_mode && !myopts.output_fn.empty()) { |
+ printf("error: get-mode does not create output files.\n"); |
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]; |
+ GoogleBinaryBlockUtil util; |
- if (!util.load_from_file(input_filename)) { |
- printf("error: cannot load valid BIOS file: %s\n", input_filename); |
+ assert(!myopts.input_fn.empty()); |
+ if (!util.load_from_file(myopts.input_fn.c_str())) { |
+ printf("error: cannot load valid BIOS file: %s\n", myopts.input_fn.c_str()); |
return err_stage; |
} |
@@ -476,53 +579,52 @@ int main(int argc, char *argv[]) { |
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()); |
+ if (opt_props.empty()) // enable hwid by default |
+ opt_props.set_new_value(GoogleBinaryBlockUtil::PROP_HWID, ""); |
+ |
+ for (OptPropertyMap::const_iterator i = opt_props.begin(); |
+ i != opt_props.end(); |
+ i++) { |
+ export_property(i->first, i->second, util); |
+ } |
+ |
} else { |
// set mode |
assert(myopts.set_mode); |
- if (opt_props < 1) { |
+ |
+ if (opt_props.empty()) { |
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()); |
+ for (OptPropertyMap::const_iterator i = opt_props.begin(); |
+ i != opt_props.end(); |
+ i++) { |
+ bool source_as_file = true; |
+ |
+ // the hwid command line parameter was a simple string. |
+ if (i->first == GoogleBinaryBlockUtil::PROP_HWID) |
+ source_as_file = false; |
+ |
+ if (!import_property(i->first, i->second, source_as_file, &util)) { |
+ printf("error: cannot set properties. abort.\n"); |
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); |
+ |
+ // use input filename (overwrite) by default |
+ if (myopts.output_fn.empty()) |
+ myopts.output_fn = myopts.input_fn; |
+ |
+ assert(!myopts.output_fn.empty()); |
+ if (!util.save_to_file(myopts.output_fn.c_str())) { |
+ printf("error: cannot save to file: %s\n", myopts.output_fn.c_str()); |
return err_stage; |
} else { |
- printf("successfully saved new image to: %s\n", output_filename); |
+ printf("successfully saved new image to: %s\n", myopts.output_fn.c_str()); |
} |
} |