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

Side by Side 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, 6 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 unified diff | Download patch
OLDNEW
(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
OLDNEW
« 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