Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 // | |
| 5 // Utility for manipulating Google Binary Block (GBB) | |
| 6 // | |
| 7 | |
| 8 #include "gbb_utility.h" | |
| 9 | |
| 10 #include <assert.h> | |
| 11 #include <getopt.h> | |
| 12 #include <stdio.h> | |
| 13 #include <string.h> | |
| 14 | |
| 15 #include <string> | |
| 16 #include <vector> | |
| 17 #include <algorithm> | |
| 18 | |
| 19 using std::string; | |
| 20 | |
| 21 /////////////////////////////////////////////////////////////////////// | |
| 22 // Simple File Utilities | |
| 23 | |
| 24 // utility function: read a non-empty file. | |
| 25 // return file content, or empty for any failure. | |
| 26 static string read_nonempty_file(const char *filename) { | |
| 27 string file_content; | |
| 28 std::vector<char> buffer; // since image files are small, should be OK | |
| 29 | |
| 30 FILE *fp = fopen(filename, "rb"); | |
| 31 if (!fp) { | |
| 32 perror(filename); | |
| 33 return file_content; | |
| 34 } | |
| 35 | |
| 36 // prepare buffer on successful seek | |
| 37 if (fseek(fp, 0, SEEK_END) == 0) { | |
| 38 buffer.resize(ftell(fp)); | |
| 39 rewind(fp); | |
| 40 } | |
| 41 | |
| 42 if (!buffer.empty()) { | |
| 43 if (fread(&buffer[0], buffer.size(), 1, fp) != 1) { | |
| 44 perror(filename); | |
| 45 buffer.clear(); // discard buffer when read fail. | |
| 46 } else { | |
| 47 file_content.assign(buffer.begin(), buffer.end()); | |
| 48 } | |
| 49 } | |
| 50 | |
| 51 fclose(fp); | |
| 52 return file_content; | |
| 53 } | |
| 54 | |
| 55 // utility function: write non-empty content to file. | |
| 56 // return true on success, otherwise false. | |
| 57 static bool write_nonempty_file(const char *filename, const string &content) { | |
| 58 assert(!content.empty()); | |
| 59 | |
| 60 FILE *fp = fopen(filename, "wb"); | |
| 61 if (!fp) { | |
| 62 perror(filename); | |
| 63 return false; | |
| 64 } | |
| 65 | |
| 66 int r = fwrite(content.c_str(), content.size(), 1, fp); | |
| 67 fclose(fp); | |
| 68 | |
| 69 if (r != 1) | |
| 70 perror(filename); | |
| 71 | |
| 72 return r == 1; | |
| 73 } | |
| 74 | |
| 75 /////////////////////////////////////////////////////////////////////// | |
| 76 // GBB Utility implementation | |
| 77 | |
| 78 namespace vboot_reference { | |
| 79 | |
| 80 GoogleBinaryBlockUtil::GoogleBinaryBlockUtil() { | |
| 81 assert(sizeof(header_) == GBB_HEADER_SIZE); | |
| 82 initialize(); | |
| 83 } | |
| 84 | |
| 85 GoogleBinaryBlockUtil::~GoogleBinaryBlockUtil() { | |
| 86 } | |
| 87 | |
| 88 void GoogleBinaryBlockUtil::initialize() { | |
| 89 verbose = true; | |
| 90 is_valid_gbb = false; | |
| 91 header_offset_ = 0; | |
| 92 memset(&header_, 0, sizeof(header_)); | |
| 93 file_content_.clear(); | |
| 94 } | |
| 95 | |
| 96 bool GoogleBinaryBlockUtil::load_from_file(const char *filename) { | |
| 97 is_valid_gbb = false; | |
| 98 | |
| 99 file_content_ = read_nonempty_file(filename); | |
| 100 if (file_content_.empty()) | |
| 101 return false; | |
| 102 | |
| 103 switch (search_header_signatures(file_content_, &header_offset_)) { | |
| 104 case 0: | |
| 105 if (verbose) | |
| 106 fprintf(stderr, " error: cannot find any GBB signature.\n"); | |
| 107 break; | |
| 108 | |
| 109 case 1: | |
| 110 // fetch a copy of block header to check more detail | |
| 111 if (!load_gbb_header(file_content_, header_offset_, &header_)) { | |
| 112 if (verbose) | |
| 113 fprintf(stderr, " error: invalid GBB in image file.\n"); | |
| 114 } else { | |
| 115 is_valid_gbb = true; | |
| 116 } | |
| 117 break; | |
| 118 | |
| 119 default: | |
| 120 if (verbose) | |
| 121 fprintf(stderr, " error: found multiple GBB signatures.\n"); | |
| 122 file_content_.clear(); | |
| 123 break; | |
| 124 } | |
| 125 | |
| 126 // discard if anything goes wrong | |
| 127 if (!is_valid_gbb) | |
| 128 initialize(); | |
| 129 | |
| 130 return is_valid_gbb; | |
| 131 } | |
| 132 | |
| 133 bool GoogleBinaryBlockUtil::save_to_file(const char *filename) { | |
| 134 assert(is_valid_gbb && !file_content_.empty()); | |
| 135 return write_nonempty_file(filename, file_content_); | |
| 136 } | |
| 137 | |
| 138 int GoogleBinaryBlockUtil::search_header_signatures(const string &image, | |
| 139 long *poffset) const { | |
| 140 int found_signatures = 0; | |
| 141 size_t last_found_pos = 0; | |
| 142 | |
| 143 while ((last_found_pos = | |
| 144 file_content_.find(GBB_SIGNATURE, last_found_pos, GBB_SIGNATURE_SIZE)) | |
| 145 != file_content_.npos) { | |
| 146 *poffset = last_found_pos; | |
| 147 found_signatures++; | |
| 148 last_found_pos++; // for next iteration | |
| 149 } | |
| 150 | |
| 151 return found_signatures; | |
| 152 } | |
| 153 | |
| 154 // uility function for load_gbb_header to check property range | |
| 155 static bool check_property_range(uint32_t off, uint32_t sz, | |
| 156 uint32_t hdr_sz, uint32_t max_sz, | |
| 157 const char *prop_name, bool verbose) { | |
| 158 if (off + sz > max_sz) { | |
| 159 if (verbose) | |
| 160 fprintf(stderr, " error: property %s exceed GBB.\n", prop_name); | |
| 161 return false; | |
| 162 } | |
| 163 | |
| 164 if (off < hdr_sz) { | |
| 165 if (verbose) | |
| 166 fprintf(stderr, " error: property %s overlap GBB header.\n", prop_name); | |
| 167 return false; | |
| 168 } | |
| 169 | |
| 170 return true; | |
| 171 } | |
| 172 | |
| 173 bool GoogleBinaryBlockUtil::load_gbb_header(const string &image, long offset, | |
| 174 GoogleBinaryBlockHeader *phdr) const { | |
| 175 assert(phdr); | |
| 176 | |
| 177 // check that GBB header does not extend past end of image | |
| 178 if (image.size() < (size_t)offset + GBB_HEADER_SIZE) { | |
| 179 if (verbose) | |
| 180 fprintf(stderr, " error: incomplete GBB.\n"); | |
| 181 return false; | |
| 182 } | |
| 183 | |
| 184 string::const_iterator block_ptr = image.begin() + offset; | |
| 185 size_t block_size = image.size() - offset; | |
| 186 | |
| 187 std::copy(block_ptr, block_ptr + GBB_HEADER_SIZE, | |
| 188 reinterpret_cast<char*>(phdr)); | |
| 189 | |
| 190 const GoogleBinaryBlockHeader &h = *phdr; // for quick access | |
| 191 | |
| 192 // check version | |
| 193 if (h.major_version != GBB_MAJOR_VER || | |
| 194 h.minor_version != GBB_MINOR_VER) { | |
| 195 if (verbose) | |
| 196 fprintf(stderr, " error: invalid GBB version (%d.%d)\n", | |
| 197 h.major_version, h.minor_version); | |
| 198 return false; | |
| 199 } | |
| 200 | |
| 201 if (h.header_size < GBB_HEADER_SIZE) { | |
| 202 if (verbose) | |
| 203 fprintf(stderr, " error: incompatible header size (%d < %d)\n", | |
| 204 h.header_size, GBB_HEADER_SIZE); | |
| 205 return false; | |
| 206 } | |
| 207 | |
| 208 // verify location of properties | |
| 209 if (!check_property_range(h.hwid_offset, h.hwid_size, | |
| 210 h.header_size, block_size, "hwid", verbose) || | |
| 211 !check_property_range(h.rootkey_offset, h.rootkey_size, | |
| 212 h.header_size, block_size, "rootkey", verbose) || | |
| 213 !check_property_range(h.bmpfv_offset, h.bmpfv_size, | |
| 214 h.header_size, block_size, "bmpfv", verbose)) { | |
| 215 return false; | |
| 216 } | |
| 217 | |
| 218 return true; | |
| 219 } | |
| 220 | |
| 221 bool GoogleBinaryBlockUtil::find_property(PROPINDEX i, | |
| 222 uint32_t *poffset, | |
| 223 uint32_t *psize) const { | |
| 224 switch (i) { | |
| 225 case PROP_HWID: | |
| 226 *poffset = header_.hwid_offset; | |
| 227 *psize = header_.hwid_size; | |
| 228 break; | |
| 229 | |
| 230 case PROP_ROOTKEY: | |
| 231 *poffset = header_.rootkey_offset; | |
| 232 *psize = header_.rootkey_size; | |
| 233 break; | |
| 234 | |
| 235 case PROP_BMPFV: | |
| 236 *poffset = header_.bmpfv_offset; | |
| 237 *psize = header_.bmpfv_size; | |
| 238 break; | |
| 239 | |
| 240 default: | |
| 241 assert(!"invalid property index."); | |
| 242 return false; | |
| 243 } | |
| 244 | |
| 245 return true; | |
| 246 } | |
| 247 | |
| 248 bool GoogleBinaryBlockUtil::set_property(PROPINDEX i, const string &value) { | |
| 249 uint32_t prop_size; | |
| 250 uint32_t prop_offset; | |
| 251 | |
| 252 assert(is_valid_gbb); | |
| 253 | |
| 254 if (!find_property(i, &prop_offset, &prop_size)) { | |
| 255 if (verbose) | |
| 256 fprintf(stderr, " internal error: unknown property (%d).\n", | |
| 257 static_cast<int>(i)); | |
| 258 return false; | |
| 259 } | |
| 260 | |
| 261 if (prop_size < value.size()) { | |
| 262 if (verbose) | |
| 263 fprintf(stderr, " error: value size (%zu) exceed capacity (%u).\n", | |
| 264 value.size(), prop_size); | |
| 265 return false; | |
| 266 } | |
| 267 | |
| 268 if (i == PROP_HWID && prop_size == value.size()) { | |
| 269 // special case: this is NUL-terminated so it's better to keep one more \0 | |
| 270 if (verbose) | |
| 271 fprintf(stderr, "error: NUL-terminated string exceed capacity (%d)\n", | |
| 272 prop_size); | |
| 273 return false; | |
| 274 } | |
| 275 | |
| 276 string::iterator dest = file_content_.begin() + header_offset_ + prop_offset; | |
| 277 file_content_.replace(dest, dest+prop_size, prop_size, '\0'); // wipe first | |
| 278 std::copy(value.begin(), value.end(), dest); | |
| 279 | |
| 280 return true; | |
| 281 } | |
| 282 | |
| 283 string GoogleBinaryBlockUtil::get_property(PROPINDEX i) const { | |
| 284 uint32_t prop_size; | |
| 285 uint32_t prop_offset; | |
| 286 | |
| 287 assert(is_valid_gbb); | |
| 288 | |
| 289 if (!find_property(i, &prop_offset, &prop_size)) { | |
| 290 if (verbose) | |
| 291 fprintf(stderr, " internal error: unknown property (%d).\n", | |
| 292 static_cast<int>(i)); | |
| 293 return ""; | |
| 294 } | |
| 295 | |
| 296 string::const_iterator dest = file_content_.begin() + | |
| 297 header_offset_ + prop_offset; | |
| 298 return string(dest, dest + prop_size); | |
| 299 } | |
| 300 | |
| 301 bool GoogleBinaryBlockUtil::set_hwid(const char *hwid) { | |
| 302 return set_property(PROP_HWID, hwid); | |
| 303 } | |
| 304 | |
| 305 bool GoogleBinaryBlockUtil::set_rootkey(const std::string &value) { | |
| 306 return set_property(PROP_ROOTKEY, value); | |
| 307 } | |
| 308 | |
| 309 bool GoogleBinaryBlockUtil::set_bmpfv(const string &value) { | |
| 310 return set_property(PROP_BMPFV, value); | |
| 311 } | |
| 312 | |
| 313 } // namespace vboot_reference | |
| 314 | |
| 315 #ifdef WITH_UTIL_MAIN | |
| 316 | |
| 317 /////////////////////////////////////////////////////////////////////// | |
| 318 // command line utilities | |
| 319 | |
| 320 // utility function: provide usage of this utility and exit. | |
| 321 static void usagehelp_exit(const char *prog_name) { | |
| 322 printf( | |
| 323 "Utility to manage Google Binary Block (GBB)\n" | |
| 324 "Usage: %s [-g|-s] [OPTIONS] bios_file [output_file]\n\n" | |
| 325 "-g, --get \tGet (read) from bios_file, " | |
| 326 "with following options:\n" | |
| 327 " --hwid \tReport hardware id (default).\n" | |
| 328 " -k, --rootkey=FILE \tFile name to export Root Key.\n" | |
| 329 " -b, --bmpfv=FILE \tFile name to export Bitmap FV.\n" | |
| 330 "\n" | |
| 331 "-s, --set \tSet (write) to bios_file, " | |
| 332 "with following options:\n" | |
| 333 " -i, --hwid=HWID \tThe new hardware id to be changed.\n" | |
| 334 " -k, --rootkey=FILE \tFile name of new Root Key.\n" | |
| 335 " -b, --bmpfv=FILE \tFile name of new Bitmap FV\n" | |
| 336 "\n" | |
| 337 " SAMPLE:\n" | |
| 338 " %s -g bios.bin\n" | |
| 339 " %s --set --hwid='New Model' -k key.bin bios.bin newbios.bin\n" | |
| 340 , prog_name, prog_name, prog_name); | |
| 341 exit(1); | |
| 342 } | |
| 343 | |
| 344 // utility function: export a property from GBB to given file. | |
| 345 // return true on success, otherwise false. | |
| 346 static bool export_property_to_file(const string &filename, | |
| 347 const char *name, const string &value) { | |
| 348 assert(!filename.empty()); | |
| 349 const char *fn = filename.c_str(); | |
| 350 | |
| 351 if (!write_nonempty_file(fn, value)) { | |
| 352 fprintf(stderr, "error: failed to export %s to file: %s\n", name, fn); | |
| 353 return false; | |
| 354 } | |
| 355 | |
| 356 printf(" - exported %s to file: %s\n", name, fn); | |
| 357 return true; | |
| 358 } | |
| 359 | |
| 360 // utility function: import a property to GBB by given file. | |
| 361 // return true on success, otherwise false. | |
| 362 // is succesfully imported into GBB. | |
| 363 static bool import_property_from_file( | |
| 364 const string &filename, const char *name, | |
| 365 bool (vboot_reference::GoogleBinaryBlockUtil::*setter)(const string &value), | |
| 366 vboot_reference::GoogleBinaryBlockUtil *putil) { | |
| 367 assert(!filename.empty()); | |
| 368 | |
| 369 printf(" - import %s from %s: ", name, filename.c_str()); | |
| 370 string v = read_nonempty_file(filename.c_str()); | |
| 371 if (v.empty()) { | |
| 372 printf("invalid file.\n"); | |
| 373 return false; | |
| 374 } | |
| 375 | |
| 376 if (!(putil->*setter)(v)) { | |
| 377 printf("invalid content.\n"); | |
| 378 return false; | |
| 379 } | |
| 380 | |
| 381 printf("success.\n"); | |
| 382 return true; | |
| 383 } | |
| 384 | |
| 385 /////////////////////////////////////////////////////////////////////// | |
| 386 // main | |
| 387 | |
| 388 int main(int argc, char *argv[]) { | |
| 389 const char *myname = argv[0]; | |
| 390 int err_stage = 0; // an indicator for error exits | |
| 391 | |
| 392 struct GBBUtilOptions { | |
| 393 bool get_mode, set_mode; | |
| 394 bool use_hwid, use_rootkey, use_bmpfv; | |
| 395 string hwid, rootkey_fn, bmpfv_fn; | |
| 396 } myopts; | |
| 397 | |
| 398 myopts.get_mode = myopts.set_mode = false; | |
| 399 myopts.use_hwid = myopts.use_rootkey = myopts.use_bmpfv = false; | |
| 400 | |
| 401 // snippets for getopt_long | |
| 402 int option_index, opt; | |
| 403 static struct option long_options[] = { | |
| 404 {"get", 0, NULL, 'g' }, | |
| 405 {"set", 0, NULL, 's' }, | |
| 406 {"hwid", 2, NULL, 'i' }, | |
| 407 {"rootkey", 1, NULL, 'k' }, | |
| 408 {"bmpfv", 1, NULL, 'b' }, | |
| 409 { NULL, 0, NULL, 0 }, | |
| 410 }; | |
| 411 int opt_props = 0; // number of assigned properties. | |
| 412 | |
| 413 // parse command line options | |
| 414 while ((opt = getopt_long(argc, argv, "gsi:k:b:", | |
| 415 long_options, &option_index)) >= 0) { | |
| 416 switch (opt) { | |
| 417 case 'g': | |
| 418 myopts.get_mode = true; | |
| 419 break; | |
| 420 | |
| 421 case 's': | |
| 422 myopts.set_mode = true; | |
| 423 break; | |
| 424 | |
| 425 case 'i': | |
| 426 opt_props++; | |
| 427 myopts.use_hwid = true; | |
| 428 if (optarg) | |
| 429 myopts.hwid = optarg; | |
| 430 break; | |
| 431 | |
| 432 case 'k': | |
| 433 opt_props++; | |
| 434 myopts.use_rootkey = true; | |
| 435 myopts.rootkey_fn = optarg; | |
| 436 break; | |
| 437 | |
| 438 case 'b': | |
| 439 opt_props++; | |
| 440 myopts.use_bmpfv = true; | |
| 441 myopts.bmpfv_fn = optarg; | |
| 442 break; | |
| 443 | |
| 444 default: | |
| 445 case '?': | |
| 446 usagehelp_exit(myname); | |
| 447 break; | |
| 448 } | |
| 449 } | |
| 450 argc -= optind; | |
| 451 argv += optind; | |
| 452 | |
| 453 // check parameters configuration | |
| 454 if (!(argc == 1 || (myopts.set_mode && argc == 2))) | |
| 455 usagehelp_exit(myname); | |
| 456 | |
| 457 // stage: parameter parsing | |
| 458 err_stage++; | |
| 459 if (myopts.get_mode == myopts.set_mode) { | |
| 460 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
| |
| 461 return err_stage; | |
| 462 } | |
| 463 | |
| 464 // stage: load image files | |
| 465 err_stage++; | |
| 466 vboot_reference::GoogleBinaryBlockUtil util; | |
| 467 const char *input_filename = argv[0], | |
| 468 *output_filename= (argc > 1) ? argv[1] : argv[0]; | |
| 469 | |
| 470 if (!util.load_from_file(input_filename)) { | |
| 471 printf("error: cannot load valid BIOS file: %s\n", input_filename); | |
| 472 return err_stage; | |
| 473 } | |
| 474 | |
| 475 // stage: processing by mode | |
| 476 err_stage++; | |
| 477 if (myopts.get_mode) { | |
| 478 // get mode | |
| 479 if (opt_props < 1) // enable hwid by default | |
| 480 myopts.use_hwid = true; | |
| 481 | |
| 482 if (myopts.use_hwid) | |
| 483 printf("Hardware ID: %s\n", util.get_hwid().c_str()); | |
| 484 if (myopts.use_rootkey) | |
| 485 export_property_to_file(myopts.rootkey_fn, "rootkey", util.get_rootkey()); | |
| 486 if (myopts.use_bmpfv) | |
| 487 export_property_to_file(myopts.bmpfv_fn, "bmpfv", util.get_bmpfv()); | |
| 488 } else { | |
| 489 // set mode | |
| 490 assert(myopts.set_mode); | |
| 491 if (opt_props < 1) { | |
| 492 printf("nothing to change. abort.\n"); | |
| 493 return err_stage; | |
| 494 } | |
| 495 | |
| 496 // HWID does not come from file, so update it direcly here. | |
| 497 if (myopts.use_hwid) { | |
| 498 string old_hwid = util.get_hwid(); | |
| 499 if (!util.set_hwid(myopts.hwid.c_str())) { | |
| 500 printf("error: inproper hardware id: %s\n", | |
| 501 myopts.hwid.c_str()); | |
| 502 return err_stage; | |
| 503 } | |
| 504 printf(" - Hardware id changed: %s -> %s.\n", | |
| 505 old_hwid.c_str(), util.get_hwid().c_str()); | |
| 506 } | |
| 507 | |
| 508 // import other properties from file | |
| 509 if ((myopts.use_rootkey && | |
| 510 !import_property_from_file(myopts.rootkey_fn, "rootkey", | |
| 511 &vboot_reference::GoogleBinaryBlockUtil::set_rootkey, &util)) || | |
| 512 (myopts.use_bmpfv && | |
| 513 !import_property_from_file(myopts.bmpfv_fn, "bmpfv", | |
| 514 &vboot_reference::GoogleBinaryBlockUtil::set_bmpfv, &util))) { | |
| 515 printf("error: cannot set new properties. abort.\n"); | |
| 516 return err_stage; | |
| 517 } | |
| 518 | |
| 519 // stage: write output | |
| 520 err_stage++; | |
| 521 if (!util.save_to_file(output_filename)) { | |
| 522 printf("error: cannot save to file: %s\n", output_filename); | |
| 523 return err_stage; | |
| 524 } else { | |
| 525 printf("successfully saved new image to: %s\n", output_filename); | |
| 526 } | |
| 527 } | |
| 528 | |
| 529 return 0; | |
| 530 } | |
| 531 | |
| 532 #endif // WITH_UTIL_MAIN | |
| 533 | |
| OLD | NEW |