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 |