blob: 2846746fa0c77ecb7ff8bf8b331d51f1ad5394a2 [file] [log] [blame]
Bill Richardsond55085d2011-02-04 15:01:37 -08001// 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 firmware screen block (BMPBLOCK) in GBB.
6//
7
8#include "bmpblk_utility.h"
Bill Richardson0a9977e2011-08-22 16:03:59 -07009#include "image_types.h"
Bill Richardsond55085d2011-02-04 15:01:37 -080010
11#include <assert.h>
12#include <errno.h>
13#include <getopt.h>
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +080014#include <lzma.h>
Bill Richardsond55085d2011-02-04 15:01:37 -080015#include <stdarg.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <string.h>
19#include <yaml.h>
20
Bill Richardson61362d62011-02-14 10:28:03 -080021extern "C" {
22#include "eficompress.h"
23}
24
25
Bill Richardsond55085d2011-02-04 15:01:37 -080026static void error(const char *format, ...) {
27 va_list ap;
28 va_start(ap, format);
29 fprintf(stderr, "ERROR: ");
30 vfprintf(stderr, format, ap);
31 va_end(ap);
32 exit(1);
33}
34
35///////////////////////////////////////////////////////////////////////
36// BmpBlock Utility implementation
37
38namespace vboot_reference {
39
Bill Richardson54e95822011-05-05 15:12:10 -070040 BmpBlockUtil::BmpBlockUtil(bool debug) {
41 major_version_ = BMPBLOCK_MAJOR_VERSION;
42 minor_version_ = BMPBLOCK_MINOR_VERSION;
43 config_.config_filename.clear();
44 memset(&config_.header, '\0', BMPBLOCK_SIGNATURE_SIZE);
45 config_.images_map.clear();
46 config_.screens_map.clear();
47 config_.localizations.clear();
48 bmpblock_.clear();
49 set_compression_ = false;
50 compression_ = COMPRESS_NONE;
51 debug_ = debug;
Bill Richardson0a9977e2011-08-22 16:03:59 -070052 render_hwid_ = true;
53 support_font_ = true;
54 got_font_ = false;
55 got_rtol_font_ = false;
Bill Richardsond55085d2011-02-04 15:01:37 -080056 }
57
Bill Richardson54e95822011-05-05 15:12:10 -070058 BmpBlockUtil::~BmpBlockUtil() {
Bill Richardsond55085d2011-02-04 15:01:37 -080059 }
Bill Richardsond55085d2011-02-04 15:01:37 -080060
Bill Richardson54e95822011-05-05 15:12:10 -070061 void BmpBlockUtil::force_compression(uint32_t compression) {
62 compression_ = compression;
63 set_compression_ = true;
64 }
Bill Richardsond55085d2011-02-04 15:01:37 -080065
Bill Richardson54e95822011-05-05 15:12:10 -070066 void BmpBlockUtil::load_from_config(const char *filename) {
67 load_yaml_config(filename);
68 fill_bmpblock_header();
69 load_all_image_files();
70 }
71
72 void BmpBlockUtil::load_yaml_config(const char *filename) {
73 yaml_parser_t parser;
74
75 config_.config_filename = filename;
76 config_.images_map.clear();
77 config_.screens_map.clear();
78 config_.localizations.clear();
Bill Richardson8ba3d792011-05-18 18:25:31 -070079 config_.locale_names.clear();
Bill Richardson54e95822011-05-05 15:12:10 -070080
81 FILE *fp = fopen(filename, "rb");
82 if (!fp) {
83 perror(filename);
84 exit(errno);
85 }
86
87 yaml_parser_initialize(&parser);
88 yaml_parser_set_input_file(&parser, fp);
89 parse_config(&parser);
90 yaml_parser_delete(&parser);
91 fclose(fp);
92
93
Bill Richardson2e022632011-08-22 14:54:14 -070094 // TODO: Check the yaml file for self-consistency. Warn on any problems.
Bill Richardson54e95822011-05-05 15:12:10 -070095 // All images should be used somewhere in the screens.
96 // All images referenced in the screens should be defined.
97 // All screens should be used somewhere in the localizations.
98 // All screens referenced in the localizations should be defined.
Bill Richardson8ba3d792011-05-18 18:25:31 -070099 // The number of localizations should match the number of locale_index
Bill Richardson54e95822011-05-05 15:12:10 -0700100
101 if (debug_) {
102 printf("%ld image_names\n", config_.image_names.size());
103 for (unsigned int i = 0; i < config_.image_names.size(); ++i) {
104 printf(" %d: \"%s\"\n", i, config_.image_names[i].c_str());
105 }
106 printf("%ld images_map\n", config_.images_map.size());
107 for (StrImageConfigMap::iterator it = config_.images_map.begin();
108 it != config_.images_map.end();
109 ++it) {
Bill Richardson0a9977e2011-08-22 16:03:59 -0700110 printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d fmt=%d\n",
Bill Richardson54e95822011-05-05 15:12:10 -0700111 it->first.c_str(),
112 it->second.filename.c_str(),
113 it->second.offset,
Bill Richardson0a9977e2011-08-22 16:03:59 -0700114 it->second.data.tag,
115 it->second.data.format);
Bill Richardson54e95822011-05-05 15:12:10 -0700116 }
117 printf("%ld screens_map\n", config_.screens_map.size());
118 for (StrScreenConfigMap::iterator it = config_.screens_map.begin();
119 it != config_.screens_map.end();
120 ++it) {
121 printf(" \"%s\":\n", it->first.c_str());
122 for (int k=0; k<MAX_IMAGE_IN_LAYOUT; k++) {
123 printf(" %d: \"%s\" (%d,%d) ofs=0x%x\n",
124 k,
125 it->second.image_names[k].c_str(),
126 it->second.data.images[k].x,
127 it->second.data.images[k].y,
128 it->second.data.images[k].image_info_offset);
129 }
130 }
131 }
132 }
133
134 void BmpBlockUtil::expect_event(yaml_parser_t *parser,
135 const yaml_event_type_e type) {
136 yaml_event_t event;
Bill Richardsond55085d2011-02-04 15:01:37 -0800137 yaml_parser_parse(parser, &event);
Bill Richardson54e95822011-05-05 15:12:10 -0700138 if (event.type != type) {
139 error("Syntax error.\n");
140 }
141 yaml_event_delete(&event);
142 }
143
144 void BmpBlockUtil::parse_config(yaml_parser_t *parser) {
145 expect_event(parser, YAML_STREAM_START_EVENT);
146 expect_event(parser, YAML_DOCUMENT_START_EVENT);
147 parse_first_layer(parser);
148 expect_event(parser, YAML_DOCUMENT_END_EVENT);
149 expect_event(parser, YAML_STREAM_END_EVENT);
150 }
151
152 void BmpBlockUtil::parse_first_layer(yaml_parser_t *parser) {
153 yaml_event_t event;
154 string keyword;
155 expect_event(parser, YAML_MAPPING_START_EVENT);
156 for (;;) {
157 yaml_parser_parse(parser, &event);
158 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800159 case YAML_SCALAR_EVENT:
160 keyword = (char*)event.data.scalar.value;
161 if (keyword == "bmpblock") {
162 parse_bmpblock(parser);
Bill Richardsona7209ee2011-02-17 14:30:14 -0800163 } else if (keyword == "compression") {
164 parse_compression(parser);
Bill Richardsond55085d2011-02-04 15:01:37 -0800165 } else if (keyword == "images") {
166 parse_images(parser);
167 } else if (keyword == "screens") {
168 parse_screens(parser);
169 } else if (keyword == "localizations") {
170 parse_localizations(parser);
Bill Richardson8ba3d792011-05-18 18:25:31 -0700171 } else if (keyword == "locale_index") {
172 parse_locale_index(parser);
Bill Richardsond55085d2011-02-04 15:01:37 -0800173 }
174 break;
175 case YAML_MAPPING_END_EVENT:
176 yaml_event_delete(&event);
177 return;
178 default:
179 error("Syntax error in parsing config file.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700180 }
181 yaml_event_delete(&event);
182 }
183 }
184
185 void BmpBlockUtil::parse_bmpblock(yaml_parser_t *parser) {
186 yaml_event_t event;
187 yaml_parser_parse(parser, &event);
188 if (event.type != YAML_SCALAR_EVENT) {
189 error("Syntax error in parsing bmpblock.\n");
190 }
191 string gotversion = (char*)event.data.scalar.value;
Bill Richardson0a9977e2011-08-22 16:03:59 -0700192 if (gotversion == "1.2") {
Bill Richardson54e95822011-05-05 15:12:10 -0700193 render_hwid_ = true;
Bill Richardson0a9977e2011-08-22 16:03:59 -0700194 support_font_ = true;
195 } else if (gotversion == "1.1") {
196 minor_version_ = 1;
197 render_hwid_ = true;
198 support_font_ = false;
199 fprintf(stderr, "WARNING: using old format: %s\n", gotversion.c_str());
Bill Richardson54e95822011-05-05 15:12:10 -0700200 } else if (gotversion == "1.0") {
201 minor_version_ = 0;
202 render_hwid_ = false;
Bill Richardson0a9977e2011-08-22 16:03:59 -0700203 support_font_ = false;
204 fprintf(stderr, "WARNING: using old format: %s\n", gotversion.c_str());
Bill Richardson54e95822011-05-05 15:12:10 -0700205 } else {
206 error("Unsupported version specified in config file (%s)\n",
207 gotversion.c_str());
Bill Richardsond55085d2011-02-04 15:01:37 -0800208 }
209 yaml_event_delete(&event);
210 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800211
Bill Richardson54e95822011-05-05 15:12:10 -0700212 void BmpBlockUtil::parse_compression(yaml_parser_t *parser) {
213 yaml_event_t event;
Bill Richardsond55085d2011-02-04 15:01:37 -0800214 yaml_parser_parse(parser, &event);
Bill Richardson54e95822011-05-05 15:12:10 -0700215 if (event.type != YAML_SCALAR_EVENT) {
216 error("Syntax error in parsing bmpblock.\n");
217 }
218 char *comp_str = (char *)event.data.scalar.value;
219 char *e = 0;
220 uint32_t comp = (uint32_t)strtoul(comp_str, &e, 0);
221 if (!*comp_str || (e && *e) || comp >= MAX_COMPRESS) {
222 error("Invalid compression specified in config file (%d)\n", comp);
223 }
224 if (!set_compression_) {
225 compression_ = comp;
226 }
227 yaml_event_delete(&event);
228 }
229
230 void BmpBlockUtil::parse_images(yaml_parser_t *parser) {
231 yaml_event_t event;
232 string image_name, image_filename;
233 expect_event(parser, YAML_MAPPING_START_EVENT);
234 for (;;) {
235 yaml_parser_parse(parser, &event);
236 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800237 case YAML_SCALAR_EVENT:
238 image_name = (char*)event.data.scalar.value;
239 yaml_event_delete(&event);
240 yaml_parser_parse(parser, &event);
241 if (event.type != YAML_SCALAR_EVENT) {
242 error("Syntax error in parsing images.\n");
243 }
244 image_filename = (char*)event.data.scalar.value;
Bill Richardsonf82f9412011-02-17 08:56:33 -0800245 config_.image_names.push_back(image_name);
Bill Richardsond55085d2011-02-04 15:01:37 -0800246 config_.images_map[image_name] = ImageConfig();
247 config_.images_map[image_name].filename = image_filename;
Bill Richardson0a9977e2011-08-22 16:03:59 -0700248 if (image_name == RENDER_HWID) {
249 got_font_ = true;
250 }
251 if (image_name == RENDER_HWID_RTOL) {
252 got_rtol_font_ = true;
253 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800254 break;
255 case YAML_MAPPING_END_EVENT:
256 yaml_event_delete(&event);
257 return;
258 default:
259 error("Syntax error in parsing images.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700260 }
261 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800262 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800263 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800264
Bill Richardson54e95822011-05-05 15:12:10 -0700265 void BmpBlockUtil::parse_layout(yaml_parser_t *parser, ScreenConfig &screen) {
266 yaml_event_t event;
267 int depth = 0, index1 = 0, index2 = 0;
268 expect_event(parser, YAML_SEQUENCE_START_EVENT);
269 for (;;) {
270 yaml_parser_parse(parser, &event);
271 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800272 case YAML_SEQUENCE_START_EVENT:
273 depth++;
274 break;
275 case YAML_SCALAR_EVENT:
276 switch (index2) {
Bill Richardson54e95822011-05-05 15:12:10 -0700277 case 0:
278 screen.data.images[index1].x = atoi((char*)event.data.scalar.value);
279 break;
280 case 1:
281 screen.data.images[index1].y = atoi((char*)event.data.scalar.value);
282 break;
283 case 2:
284 screen.image_names[index1] = (char*)event.data.scalar.value;
285 // Detect the special case where we're rendering the HWID string
Bill Richardson0a9977e2011-08-22 16:03:59 -0700286 // instead of displaying a bitmap. The image name may not
287 // exist in the list of images (v1.1), but we will still need an
Bill Richardson54e95822011-05-05 15:12:10 -0700288 // ImageInfo struct to remember where to draw the text.
Bill Richardson0a9977e2011-08-22 16:03:59 -0700289 // Note that v1.2 requires that the image name DOES exist, because
290 // the corresponding file is used to hold the font glpyhs.
Bill Richardson54e95822011-05-05 15:12:10 -0700291 if (render_hwid_) {
292 if (screen.image_names[index1] == RENDER_HWID) {
293 config_.images_map[RENDER_HWID].data.tag = TAG_HWID;
Bill Richardson0a9977e2011-08-22 16:03:59 -0700294 if (support_font_ && !got_font_)
295 error("Font required in 'image:' section for %s\n",
296 RENDER_HWID);
Bill Richardson54e95822011-05-05 15:12:10 -0700297 } else if (screen.image_names[index1] == RENDER_HWID_RTOL) {
298 config_.images_map[RENDER_HWID_RTOL].data.tag = TAG_HWID_RTOL;
Bill Richardson0a9977e2011-08-22 16:03:59 -0700299 if (support_font_ && !got_rtol_font_)
300 error("Font required in 'image:' section for %s\n",
301 RENDER_HWID_RTOL);
Bill Richardson54e95822011-05-05 15:12:10 -0700302 }
303 }
304 break;
305 default:
306 error("Syntax error in parsing layout\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800307 }
308 index2++;
309 break;
310 case YAML_SEQUENCE_END_EVENT:
311 if (depth == 1) {
312 index1++;
313 index2 = 0;
314 } else if (depth == 0) {
315 yaml_event_delete(&event);
316 return;
317 }
318 depth--;
319 break;
320 default:
321 error("Syntax error in paring layout.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700322 }
323 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800324 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800325 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800326
Bill Richardson54e95822011-05-05 15:12:10 -0700327 void BmpBlockUtil::parse_screens(yaml_parser_t *parser) {
328 yaml_event_t event;
329 string screen_name;
330 expect_event(parser, YAML_MAPPING_START_EVENT);
331 for (;;) {
332 yaml_parser_parse(parser, &event);
333 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800334 case YAML_SCALAR_EVENT:
335 screen_name = (char*)event.data.scalar.value;
336 config_.screens_map[screen_name] = ScreenConfig();
337 parse_layout(parser, config_.screens_map[screen_name]);
338 break;
339 case YAML_MAPPING_END_EVENT:
340 yaml_event_delete(&event);
341 return;
342 default:
343 error("Syntax error in parsing screens.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700344 }
345 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800346 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800347 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800348
Bill Richardson54e95822011-05-05 15:12:10 -0700349 void BmpBlockUtil::parse_localizations(yaml_parser_t *parser) {
350 yaml_event_t event;
351 int depth = 0, index = 0;
352 expect_event(parser, YAML_SEQUENCE_START_EVENT);
353 for (;;) {
354 yaml_parser_parse(parser, &event);
355 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800356 case YAML_SEQUENCE_START_EVENT:
357 config_.localizations.push_back(vector<string>());
358 depth++;
359 break;
360 case YAML_SCALAR_EVENT:
361 config_.localizations[index].push_back((char*)event.data.scalar.value);
362 break;
363 case YAML_SEQUENCE_END_EVENT:
364 if (depth == 1) {
365 index++;
366 } else if (depth == 0) {
367 yaml_event_delete(&event);
368 return;
369 }
370 depth--;
371 break;
372 default:
373 error("Syntax error in parsing localizations.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700374 }
375 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800376 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800377 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800378
Bill Richardson8ba3d792011-05-18 18:25:31 -0700379 void BmpBlockUtil::parse_locale_index(yaml_parser_t *parser) {
380 yaml_event_t event;
381 expect_event(parser, YAML_SEQUENCE_START_EVENT);
382 for (;;) {
383 yaml_parser_parse(parser, &event);
384 switch (event.type) {
385 case YAML_SCALAR_EVENT:
386 config_.locale_names.append((char*)event.data.scalar.value);
387 config_.locale_names.append(1, (char)'\0'); // '\0' to delimit
388 break;
389 case YAML_SEQUENCE_END_EVENT:
390 yaml_event_delete(&event);
391 config_.locale_names.append(1, (char)'\0'); // double '\0' to terminate
392 return;
393 default:
394 error("Syntax error in parsing localizations.\n");
395 }
396 }
397 }
398
Bill Richardson54e95822011-05-05 15:12:10 -0700399 void BmpBlockUtil::load_all_image_files() {
400 for (unsigned int i = 0; i < config_.image_names.size(); i++) {
401 StrImageConfigMap::iterator it =
402 config_.images_map.find(config_.image_names[i]);
403 if (debug_) {
404 printf("loading image \"%s\" from \"%s\"\n",
405 config_.image_names[i].c_str(),
406 it->second.filename.c_str());
407 }
408 const string &content = read_image_file(it->second.filename.c_str());
409 it->second.raw_content = content;
410 it->second.data.original_size = content.size();
Bill Richardson0a9977e2011-08-22 16:03:59 -0700411 it->second.data.format =
412 identify_image_type(content.c_str(),
413 (uint32_t)content.size(), &it->second.data);
414 if (FORMAT_INVALID == it->second.data.format) {
Bill Richardson54e95822011-05-05 15:12:10 -0700415 error("Unsupported image format in %s\n", it->second.filename.c_str());
416 }
417 switch(compression_) {
418 case COMPRESS_NONE:
419 it->second.data.compression = compression_;
420 it->second.compressed_content = content;
421 it->second.data.compressed_size = content.size();
422 break;
423 case COMPRESS_EFIv1:
Bill Richardson61362d62011-02-14 10:28:03 -0800424 {
425 // The content will always compress smaller (so sez the docs).
426 uint32_t tmpsize = content.size();
427 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
428 // The size of the compressed content is also returned.
429 if (EFI_SUCCESS != EfiCompress((uint8_t *)content.c_str(), tmpsize,
430 tmpbuf, &tmpsize)) {
431 error("Unable to compress!\n");
432 }
433 it->second.data.compression = compression_;
434 it->second.compressed_content.assign((const char *)tmpbuf, tmpsize);
435 it->second.data.compressed_size = tmpsize;
436 free(tmpbuf);
437 }
438 break;
Bill Richardson54e95822011-05-05 15:12:10 -0700439 case COMPRESS_LZMA1:
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800440 {
441 // Calculate the worst case of buffer size.
442 uint32_t tmpsize = lzma_stream_buffer_bound(content.size());
443 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
444 lzma_stream stream = LZMA_STREAM_INIT;
445 lzma_options_lzma options;
446 lzma_ret result;
447
448 lzma_lzma_preset(&options, 9);
449 result = lzma_alone_encoder(&stream, &options);
450 if (result != LZMA_OK) {
451 error("Unable to initialize easy encoder (error: %d)!\n", result);
452 }
453
454 stream.next_in = (uint8_t *)content.data();
455 stream.avail_in = content.size();
456 stream.next_out = tmpbuf;
457 stream.avail_out = tmpsize;
458 result = lzma_code(&stream, LZMA_FINISH);
459 if (result != LZMA_STREAM_END) {
460 error("Unable to encode data (error: %d)!\n", result);
461 }
462
463 it->second.data.compression = compression_;
464 it->second.compressed_content.assign((const char *)tmpbuf,
465 tmpsize - stream.avail_out);
466 it->second.data.compressed_size = tmpsize - stream.avail_out;
467 lzma_end(&stream);
468 free(tmpbuf);
469 }
470 break;
Bill Richardsond55085d2011-02-04 15:01:37 -0800471 default:
Bill Richardson54e95822011-05-05 15:12:10 -0700472 error("Unsupported compression method attempted.\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800473 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800474 }
475 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800476
Bill Richardson54e95822011-05-05 15:12:10 -0700477 const string BmpBlockUtil::read_image_file(const char *filename) {
478 string content;
479 vector<char> buffer;
Bill Richardsond55085d2011-02-04 15:01:37 -0800480
Bill Richardson54e95822011-05-05 15:12:10 -0700481 FILE *fp = fopen(filename, "rb");
482 if (!fp) {
483 perror(filename);
484 exit(errno);
485 }
486
487 if (fseek(fp, 0, SEEK_END) == 0) {
488 buffer.resize(ftell(fp));
489 rewind(fp);
490 }
491
492 if (!buffer.empty()) {
493 if(fread(&buffer[0], buffer.size(), 1, fp) != 1) {
494 perror(filename);
495 buffer.clear();
496 } else {
497 content.assign(buffer.begin(), buffer.end());
498 }
499 }
500
501 fclose(fp);
502 return content;
503 }
504
Bill Richardson54e95822011-05-05 15:12:10 -0700505 void BmpBlockUtil::fill_bmpblock_header() {
506 memset(&config_.header, '\0', sizeof(config_.header));
507 memcpy(&config_.header.signature, BMPBLOCK_SIGNATURE,
508 BMPBLOCK_SIGNATURE_SIZE);
509 config_.header.major_version = major_version_;
510 config_.header.minor_version = minor_version_;
511 config_.header.number_of_localizations = config_.localizations.size();
512 config_.header.number_of_screenlayouts = config_.localizations[0].size();
513 // HEY: this is part of the yaml consistency check
514 for (unsigned int i = 1; i < config_.localizations.size(); ++i) {
515 assert(config_.header.number_of_screenlayouts ==
516 config_.localizations[i].size());
517 }
518 config_.header.number_of_imageinfos = config_.images_map.size();
Bill Richardson8ba3d792011-05-18 18:25:31 -0700519 config_.header.locale_string_offset = 0; // Filled by pack_bmpblock()
Bill Richardson54e95822011-05-05 15:12:10 -0700520 }
521
522 void BmpBlockUtil::pack_bmpblock() {
523 bmpblock_.clear();
524
525 /* Compute the ImageInfo offsets from start of BMPBLOCK. */
526 uint32_t current_offset = sizeof(BmpBlockHeader) +
527 sizeof(ScreenLayout) * (config_.header.number_of_localizations *
528 config_.header.number_of_screenlayouts);
529 for (StrImageConfigMap::iterator it = config_.images_map.begin();
530 it != config_.images_map.end();
531 ++it) {
532 it->second.offset = current_offset;
533 if (debug_)
Bill Richardson0a9977e2011-08-22 16:03:59 -0700534 printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d fmt=%d\n",
Bill Richardson54e95822011-05-05 15:12:10 -0700535 it->first.c_str(),
536 it->second.filename.c_str(),
537 it->second.offset,
Bill Richardson0a9977e2011-08-22 16:03:59 -0700538 it->second.data.tag,
539 it->second.data.format);
Bill Richardson54e95822011-05-05 15:12:10 -0700540 current_offset += sizeof(ImageInfo) +
541 it->second.data.compressed_size;
542 /* Make it 4-byte aligned. */
543 if ((current_offset & 3) > 0) {
544 current_offset = (current_offset & ~3) + 4;
545 }
546 }
Bill Richardson8ba3d792011-05-18 18:25:31 -0700547 /* And leave room for the locale_index string */
548 if (config_.locale_names.size()) {
549 config_.header.locale_string_offset = current_offset;
550 current_offset += config_.locale_names.size();
551 }
552
Bill Richardson54e95822011-05-05 15:12:10 -0700553 bmpblock_.resize(current_offset);
554
555 /* Fill BmpBlockHeader struct. */
556 string::iterator current_filled = bmpblock_.begin();
557 std::copy(reinterpret_cast<char*>(&config_.header),
558 reinterpret_cast<char*>(&config_.header + 1),
559 current_filled);
560 current_filled += sizeof(config_.header);
561 current_offset = sizeof(config_.header);
562
563 /* Fill all ScreenLayout structs. */
564 for (unsigned int i = 0; i < config_.localizations.size(); ++i) {
565 for (unsigned int j = 0; j < config_.localizations[i].size(); ++j) {
566 ScreenConfig &screen = config_.screens_map[config_.localizations[i][j]];
567 for (unsigned int k = 0;
568 k < MAX_IMAGE_IN_LAYOUT && !screen.image_names[k].empty();
569 ++k) {
570 if (config_.images_map.find(screen.image_names[k]) ==
571 config_.images_map.end()) {
572 error("Invalid image name \"%s\"\n", screen.image_names[k].c_str());
573 }
574 if (debug_)
575 printf("i=%d j=%d k=%d=\"%s\" (%d,%d) ofs=%x\n", i,j,k,
576 screen.image_names[k].c_str(),
577 screen.data.images[k].x, screen.data.images[k].y,
578 config_.images_map[screen.image_names[k]].offset
579 );
580 screen.data.images[k].image_info_offset =
Bill Richardsond55085d2011-02-04 15:01:37 -0800581 config_.images_map[screen.image_names[k]].offset;
Bill Richardson54e95822011-05-05 15:12:10 -0700582 }
583 std::copy(reinterpret_cast<char*>(&screen.data),
584 reinterpret_cast<char*>(&screen.data + 1),
585 current_filled);
586 current_filled += sizeof(screen.data);
587 if (debug_)
588 printf("S: current offset is 0x%08x\n", current_offset);
589 current_offset += sizeof(screen.data);
Bill Richardsond55085d2011-02-04 15:01:37 -0800590 }
Bill Richardson54e95822011-05-05 15:12:10 -0700591 }
592
593 /* Fill all ImageInfo structs and image contents. */
594 for (StrImageConfigMap::iterator it = config_.images_map.begin();
595 it != config_.images_map.end();
596 ++it) {
597 current_filled = bmpblock_.begin() + it->second.offset;
598 current_offset = it->second.offset;
599 if (debug_)
600 printf("I0: current offset is 0x%08x\n", current_offset);
601 std::copy(reinterpret_cast<char*>(&it->second.data),
602 reinterpret_cast<char*>(&it->second.data + 1),
Bill Richardsond55085d2011-02-04 15:01:37 -0800603 current_filled);
Bill Richardson54e95822011-05-05 15:12:10 -0700604 current_filled += sizeof(it->second.data);
605 current_offset += sizeof(it->second.data);
606 if (debug_)
607 printf("I1: current offset is 0x%08x (len %ld)\n",
608 current_offset, it->second.compressed_content.length());
609 std::copy(it->second.compressed_content.begin(),
610 it->second.compressed_content.end(),
611 current_filled);
Bill Richardsond55085d2011-02-04 15:01:37 -0800612 }
Bill Richardson8ba3d792011-05-18 18:25:31 -0700613
614 /* Fill in locale_names. */
615 if (config_.header.locale_string_offset) {
616 current_offset = config_.header.locale_string_offset;
617 current_filled = bmpblock_.begin() + current_offset;
618 if (debug_)
619 printf("locale_names: offset 0x%08x (len %ld)\n",
620 current_offset, config_.locale_names.size());
621 std::copy(config_.locale_names.begin(),
622 config_.locale_names.end(),
623 current_filled);
624 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800625 }
626
Bill Richardson54e95822011-05-05 15:12:10 -0700627 void BmpBlockUtil::write_to_bmpblock(const char *filename) {
628 assert(!bmpblock_.empty());
Bill Richardsond55085d2011-02-04 15:01:37 -0800629
Bill Richardson54e95822011-05-05 15:12:10 -0700630 FILE *fp = fopen(filename, "wb");
631 if (!fp) {
632 perror(filename);
633 exit(errno);
634 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800635
Bill Richardson54e95822011-05-05 15:12:10 -0700636 int r = fwrite(bmpblock_.c_str(), bmpblock_.size(), 1, fp);
637 fclose(fp);
638 if (r != 1) {
639 perror(filename);
640 exit(errno);
641 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800642 }
643
Bill Richardsond55085d2011-02-04 15:01:37 -0800644} // namespace vboot_reference
645
646#ifdef WITH_UTIL_MAIN
647
Bill Richardson54e95822011-05-05 15:12:10 -0700648 //////////////////////////////////////////////////////////////////////////////
649 // Command line utilities.
Bill Richardson794d4d42011-02-10 19:13:10 -0800650
Bill Richardson54e95822011-05-05 15:12:10 -0700651 extern "C" {
Bill Richardson794d4d42011-02-10 19:13:10 -0800652#include "bmpblk_util.h"
Bill Richardson54e95822011-05-05 15:12:10 -0700653 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800654
Bill Richardson54e95822011-05-05 15:12:10 -0700655 using vboot_reference::BmpBlockUtil;
Bill Richardsond55085d2011-02-04 15:01:37 -0800656
Bill Richardson54e95822011-05-05 15:12:10 -0700657 // utility function: provide usage of this utility and exit.
658 static void usagehelp_exit(const char *prog_name) {
659 printf(
660 "\n"
661 "To create a new BMPBLOCK file using config from YAML file:\n"
662 "\n"
663 " %s [-z NUM] -c YAML BMPBLOCK\n"
664 "\n"
665 " -z NUM = compression algorithm to use\n"
666 " 0 = none\n"
667 " 1 = EFIv1\n"
668 " 2 = LZMA1\n"
669 "\n", prog_name);
670 printf(
671 "To display the contents of a BMPBLOCK:\n"
672 "\n"
673 " %s [-y] BMPBLOCK\n"
674 "\n"
675 " -y = display as yaml\n"
676 "\n", prog_name);
677 printf(
678 "To unpack a BMPBLOCK file:\n"
679 "\n"
680 " %s -x [-d DIR] [-f] BMPBLOCK\n"
681 "\n"
682 " -d DIR = directory to use (default '.')\n"
683 " -f = force overwriting existing files\n"
684 "\n", prog_name);
685 exit(1);
686 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800687
Bill Richardson54e95822011-05-05 15:12:10 -0700688 ///////////////////////////////////////////////////////////////////////
689 // main
Bill Richardsond55085d2011-02-04 15:01:37 -0800690
Bill Richardson54e95822011-05-05 15:12:10 -0700691 int main(int argc, char *argv[]) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800692
Bill Richardson54e95822011-05-05 15:12:10 -0700693 const char *prog_name = strrchr(argv[0], '/');
694 if (prog_name)
695 prog_name++;
696 else
697 prog_name = argv[0];
Bill Richardsond55085d2011-02-04 15:01:37 -0800698
Bill Richardson54e95822011-05-05 15:12:10 -0700699 int overwrite = 0, extract_mode = 0;
700 int compression = 0;
701 int set_compression = 0;
702 const char *config_fn = 0, *bmpblock_fn = 0, *extract_dir = ".";
703 int show_as_yaml = 0;
704 bool debug = false;
Bill Richardsond55085d2011-02-04 15:01:37 -0800705
Bill Richardson54e95822011-05-05 15:12:10 -0700706 int opt;
707 opterr = 0; // quiet
708 int errorcnt = 0;
709 char *e = 0;
710 while ((opt = getopt(argc, argv, ":c:xz:fd:yD")) != -1) {
711 switch (opt) {
712 case 'c':
713 config_fn = optarg;
714 break;
715 case 'x':
716 extract_mode = 1;
717 break;
718 case 'y':
719 show_as_yaml = 1;
720 break;
721 case 'z':
722 compression = (int)strtoul(optarg, &e, 0);
723 if (!*optarg || (e && *e)) {
724 fprintf(stderr, "%s: invalid argument to -%c: \"%s\"\n",
725 prog_name, opt, optarg);
726 errorcnt++;
727 }
728 if (compression >= MAX_COMPRESS) {
729 fprintf(stderr, "%s: compression type must be less than %d\n",
730 prog_name, MAX_COMPRESS);
731 errorcnt++;
732 }
733 set_compression = 1;
734 break;
735 case 'f':
736 overwrite = 1;
737 break;
738 case 'd':
739 extract_dir= optarg;
740 break;
741 case 'D':
742 debug = true;
743 break;
744 case ':':
745 fprintf(stderr, "%s: missing argument to -%c\n",
746 prog_name, optopt);
Bill Richardson794d4d42011-02-10 19:13:10 -0800747 errorcnt++;
Bill Richardson54e95822011-05-05 15:12:10 -0700748 break;
749 default:
750 fprintf(stderr, "%s: unrecognized switch: -%c\n",
751 prog_name, optopt);
Bill Richardson794d4d42011-02-10 19:13:10 -0800752 errorcnt++;
Bill Richardson54e95822011-05-05 15:12:10 -0700753 break;
Bill Richardson794d4d42011-02-10 19:13:10 -0800754 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800755 }
Bill Richardson54e95822011-05-05 15:12:10 -0700756 argc -= optind;
757 argv += optind;
758
759 if (argc >= 1) {
760 bmpblock_fn = argv[0];
761 } else {
762 fprintf(stderr, "%s: missing BMPBLOCK name\n", prog_name);
763 errorcnt++;
764 }
765
766 if (errorcnt)
767 usagehelp_exit(prog_name);
768
769 BmpBlockUtil util(debug);
770
771 if (config_fn) {
772 if (set_compression)
773 util.force_compression(compression);
774 util.load_from_config(config_fn);
775 util.pack_bmpblock();
776 util.write_to_bmpblock(bmpblock_fn);
777 }
778
779 else if (extract_mode) {
780 return dump_bmpblock(bmpblock_fn, 1, extract_dir, overwrite);
781 } else {
782 return dump_bmpblock(bmpblock_fn, show_as_yaml, 0, 0);
783 }
784
785 return 0;
Bill Richardsond55085d2011-02-04 15:01:37 -0800786 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800787
788#endif // WITH_UTIL_MAIN