blob: 5069e4a87bfde008f8f4418f1194ca56399fb1fb [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
Bill Richardsond55085d2011-02-04 15:01:37 -08008#include <assert.h>
9#include <errno.h>
10#include <getopt.h>
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +080011#include <lzma.h>
Bill Richardsond55085d2011-02-04 15:01:37 -080012#include <stdarg.h>
Bill Richardson0c3ba242013-03-29 11:09:30 -070013#include <stdint.h>
Bill Richardsond55085d2011-02-04 15:01:37 -080014#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <yaml.h>
18
Bill Richardson0c3ba242013-03-29 11:09:30 -070019#include "bmpblk_utility.h"
20#include "image_types.h"
21#include "vboot_api.h"
22
Bill Richardson61362d62011-02-14 10:28:03 -080023extern "C" {
24#include "eficompress.h"
25}
26
27
Bill Richardsond55085d2011-02-04 15:01:37 -080028static void error(const char *format, ...) {
29 va_list ap;
30 va_start(ap, format);
31 fprintf(stderr, "ERROR: ");
32 vfprintf(stderr, format, ap);
33 va_end(ap);
34 exit(1);
35}
36
37///////////////////////////////////////////////////////////////////////
38// BmpBlock Utility implementation
39
40namespace vboot_reference {
41
Bill Richardson54e95822011-05-05 15:12:10 -070042 BmpBlockUtil::BmpBlockUtil(bool debug) {
43 major_version_ = BMPBLOCK_MAJOR_VERSION;
44 minor_version_ = BMPBLOCK_MINOR_VERSION;
45 config_.config_filename.clear();
46 memset(&config_.header, '\0', BMPBLOCK_SIGNATURE_SIZE);
47 config_.images_map.clear();
48 config_.screens_map.clear();
49 config_.localizations.clear();
50 bmpblock_.clear();
51 set_compression_ = false;
52 compression_ = COMPRESS_NONE;
53 debug_ = debug;
Bill Richardson0a9977e2011-08-22 16:03:59 -070054 render_hwid_ = true;
55 support_font_ = true;
56 got_font_ = false;
57 got_rtol_font_ = false;
Bill Richardsond55085d2011-02-04 15:01:37 -080058 }
59
Bill Richardson54e95822011-05-05 15:12:10 -070060 BmpBlockUtil::~BmpBlockUtil() {
Bill Richardsond55085d2011-02-04 15:01:37 -080061 }
Bill Richardsond55085d2011-02-04 15:01:37 -080062
Bill Richardson54e95822011-05-05 15:12:10 -070063 void BmpBlockUtil::force_compression(uint32_t compression) {
64 compression_ = compression;
65 set_compression_ = true;
66 }
Bill Richardsond55085d2011-02-04 15:01:37 -080067
Bill Richardson54e95822011-05-05 15:12:10 -070068 void BmpBlockUtil::load_from_config(const char *filename) {
69 load_yaml_config(filename);
70 fill_bmpblock_header();
71 load_all_image_files();
72 }
73
74 void BmpBlockUtil::load_yaml_config(const char *filename) {
75 yaml_parser_t parser;
76
77 config_.config_filename = filename;
78 config_.images_map.clear();
79 config_.screens_map.clear();
80 config_.localizations.clear();
Bill Richardson8ba3d792011-05-18 18:25:31 -070081 config_.locale_names.clear();
Bill Richardson54e95822011-05-05 15:12:10 -070082
83 FILE *fp = fopen(filename, "rb");
84 if (!fp) {
85 perror(filename);
86 exit(errno);
87 }
88
89 yaml_parser_initialize(&parser);
90 yaml_parser_set_input_file(&parser, fp);
91 parse_config(&parser);
92 yaml_parser_delete(&parser);
93 fclose(fp);
94
95
Bill Richardson2e022632011-08-22 14:54:14 -070096 // TODO: Check the yaml file for self-consistency. Warn on any problems.
Bill Richardson54e95822011-05-05 15:12:10 -070097 // All images should be used somewhere in the screens.
98 // All images referenced in the screens should be defined.
99 // All screens should be used somewhere in the localizations.
100 // All screens referenced in the localizations should be defined.
Bill Richardson8ba3d792011-05-18 18:25:31 -0700101 // The number of localizations should match the number of locale_index
Bill Richardson54e95822011-05-05 15:12:10 -0700102
103 if (debug_) {
104 printf("%ld image_names\n", config_.image_names.size());
105 for (unsigned int i = 0; i < config_.image_names.size(); ++i) {
106 printf(" %d: \"%s\"\n", i, config_.image_names[i].c_str());
107 }
108 printf("%ld images_map\n", config_.images_map.size());
109 for (StrImageConfigMap::iterator it = config_.images_map.begin();
110 it != config_.images_map.end();
111 ++it) {
Bill Richardson0a9977e2011-08-22 16:03:59 -0700112 printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d fmt=%d\n",
Bill Richardson54e95822011-05-05 15:12:10 -0700113 it->first.c_str(),
114 it->second.filename.c_str(),
115 it->second.offset,
Bill Richardson0a9977e2011-08-22 16:03:59 -0700116 it->second.data.tag,
117 it->second.data.format);
Bill Richardson54e95822011-05-05 15:12:10 -0700118 }
119 printf("%ld screens_map\n", config_.screens_map.size());
120 for (StrScreenConfigMap::iterator it = config_.screens_map.begin();
121 it != config_.screens_map.end();
122 ++it) {
123 printf(" \"%s\":\n", it->first.c_str());
124 for (int k=0; k<MAX_IMAGE_IN_LAYOUT; k++) {
125 printf(" %d: \"%s\" (%d,%d) ofs=0x%x\n",
126 k,
127 it->second.image_names[k].c_str(),
128 it->second.data.images[k].x,
129 it->second.data.images[k].y,
130 it->second.data.images[k].image_info_offset);
131 }
132 }
133 }
134 }
135
136 void BmpBlockUtil::expect_event(yaml_parser_t *parser,
137 const yaml_event_type_e type) {
138 yaml_event_t event;
Bill Richardsond55085d2011-02-04 15:01:37 -0800139 yaml_parser_parse(parser, &event);
Bill Richardson54e95822011-05-05 15:12:10 -0700140 if (event.type != type) {
141 error("Syntax error.\n");
142 }
143 yaml_event_delete(&event);
144 }
145
146 void BmpBlockUtil::parse_config(yaml_parser_t *parser) {
147 expect_event(parser, YAML_STREAM_START_EVENT);
148 expect_event(parser, YAML_DOCUMENT_START_EVENT);
149 parse_first_layer(parser);
150 expect_event(parser, YAML_DOCUMENT_END_EVENT);
151 expect_event(parser, YAML_STREAM_END_EVENT);
152 }
153
154 void BmpBlockUtil::parse_first_layer(yaml_parser_t *parser) {
155 yaml_event_t event;
156 string keyword;
157 expect_event(parser, YAML_MAPPING_START_EVENT);
158 for (;;) {
159 yaml_parser_parse(parser, &event);
160 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800161 case YAML_SCALAR_EVENT:
162 keyword = (char*)event.data.scalar.value;
163 if (keyword == "bmpblock") {
164 parse_bmpblock(parser);
Bill Richardsona7209ee2011-02-17 14:30:14 -0800165 } else if (keyword == "compression") {
166 parse_compression(parser);
Bill Richardsond55085d2011-02-04 15:01:37 -0800167 } else if (keyword == "images") {
168 parse_images(parser);
169 } else if (keyword == "screens") {
170 parse_screens(parser);
171 } else if (keyword == "localizations") {
172 parse_localizations(parser);
Bill Richardson8ba3d792011-05-18 18:25:31 -0700173 } else if (keyword == "locale_index") {
174 parse_locale_index(parser);
Bill Richardsond55085d2011-02-04 15:01:37 -0800175 }
176 break;
177 case YAML_MAPPING_END_EVENT:
178 yaml_event_delete(&event);
179 return;
180 default:
181 error("Syntax error in parsing config file.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700182 }
183 yaml_event_delete(&event);
184 }
185 }
186
187 void BmpBlockUtil::parse_bmpblock(yaml_parser_t *parser) {
188 yaml_event_t event;
189 yaml_parser_parse(parser, &event);
190 if (event.type != YAML_SCALAR_EVENT) {
191 error("Syntax error in parsing bmpblock.\n");
192 }
193 string gotversion = (char*)event.data.scalar.value;
Dave Parker3985f942012-08-01 08:16:38 -0700194 if (gotversion != "2.0") {
Bill Richardson54e95822011-05-05 15:12:10 -0700195 error("Unsupported version specified in config file (%s)\n",
196 gotversion.c_str());
Bill Richardsond55085d2011-02-04 15:01:37 -0800197 }
198 yaml_event_delete(&event);
199 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800200
Bill Richardson54e95822011-05-05 15:12:10 -0700201 void BmpBlockUtil::parse_compression(yaml_parser_t *parser) {
202 yaml_event_t event;
Bill Richardsond55085d2011-02-04 15:01:37 -0800203 yaml_parser_parse(parser, &event);
Bill Richardson54e95822011-05-05 15:12:10 -0700204 if (event.type != YAML_SCALAR_EVENT) {
205 error("Syntax error in parsing bmpblock.\n");
206 }
207 char *comp_str = (char *)event.data.scalar.value;
208 char *e = 0;
209 uint32_t comp = (uint32_t)strtoul(comp_str, &e, 0);
210 if (!*comp_str || (e && *e) || comp >= MAX_COMPRESS) {
211 error("Invalid compression specified in config file (%d)\n", comp);
212 }
213 if (!set_compression_) {
214 compression_ = comp;
215 }
216 yaml_event_delete(&event);
217 }
218
219 void BmpBlockUtil::parse_images(yaml_parser_t *parser) {
220 yaml_event_t event;
221 string image_name, image_filename;
222 expect_event(parser, YAML_MAPPING_START_EVENT);
223 for (;;) {
224 yaml_parser_parse(parser, &event);
225 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800226 case YAML_SCALAR_EVENT:
227 image_name = (char*)event.data.scalar.value;
228 yaml_event_delete(&event);
229 yaml_parser_parse(parser, &event);
230 if (event.type != YAML_SCALAR_EVENT) {
231 error("Syntax error in parsing images.\n");
232 }
233 image_filename = (char*)event.data.scalar.value;
Bill Richardsonf82f9412011-02-17 08:56:33 -0800234 config_.image_names.push_back(image_name);
Bill Richardsond55085d2011-02-04 15:01:37 -0800235 config_.images_map[image_name] = ImageConfig();
236 config_.images_map[image_name].filename = image_filename;
Bill Richardson0a9977e2011-08-22 16:03:59 -0700237 if (image_name == RENDER_HWID) {
238 got_font_ = true;
239 }
240 if (image_name == RENDER_HWID_RTOL) {
241 got_rtol_font_ = true;
242 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800243 break;
244 case YAML_MAPPING_END_EVENT:
245 yaml_event_delete(&event);
246 return;
247 default:
248 error("Syntax error in parsing images.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700249 }
250 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800251 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800252 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800253
Bill Richardson54e95822011-05-05 15:12:10 -0700254 void BmpBlockUtil::parse_layout(yaml_parser_t *parser, ScreenConfig &screen) {
255 yaml_event_t event;
256 int depth = 0, index1 = 0, index2 = 0;
257 expect_event(parser, YAML_SEQUENCE_START_EVENT);
258 for (;;) {
259 yaml_parser_parse(parser, &event);
260 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800261 case YAML_SEQUENCE_START_EVENT:
262 depth++;
263 break;
264 case YAML_SCALAR_EVENT:
265 switch (index2) {
Bill Richardson54e95822011-05-05 15:12:10 -0700266 case 0:
267 screen.data.images[index1].x = atoi((char*)event.data.scalar.value);
268 break;
269 case 1:
270 screen.data.images[index1].y = atoi((char*)event.data.scalar.value);
271 break;
272 case 2:
273 screen.image_names[index1] = (char*)event.data.scalar.value;
274 // Detect the special case where we're rendering the HWID string
Bill Richardson0a9977e2011-08-22 16:03:59 -0700275 // instead of displaying a bitmap. The image name may not
276 // exist in the list of images (v1.1), but we will still need an
Bill Richardson54e95822011-05-05 15:12:10 -0700277 // ImageInfo struct to remember where to draw the text.
Bill Richardson0a9977e2011-08-22 16:03:59 -0700278 // Note that v1.2 requires that the image name DOES exist, because
279 // the corresponding file is used to hold the font glpyhs.
Bill Richardson54e95822011-05-05 15:12:10 -0700280 if (render_hwid_) {
281 if (screen.image_names[index1] == RENDER_HWID) {
282 config_.images_map[RENDER_HWID].data.tag = TAG_HWID;
Bill Richardson0a9977e2011-08-22 16:03:59 -0700283 if (support_font_ && !got_font_)
284 error("Font required in 'image:' section for %s\n",
285 RENDER_HWID);
Bill Richardson54e95822011-05-05 15:12:10 -0700286 } else if (screen.image_names[index1] == RENDER_HWID_RTOL) {
287 config_.images_map[RENDER_HWID_RTOL].data.tag = TAG_HWID_RTOL;
Bill Richardson0a9977e2011-08-22 16:03:59 -0700288 if (support_font_ && !got_rtol_font_)
289 error("Font required in 'image:' section for %s\n",
290 RENDER_HWID_RTOL);
Bill Richardson54e95822011-05-05 15:12:10 -0700291 }
292 }
293 break;
294 default:
295 error("Syntax error in parsing layout\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800296 }
297 index2++;
298 break;
299 case YAML_SEQUENCE_END_EVENT:
300 if (depth == 1) {
301 index1++;
302 index2 = 0;
303 } else if (depth == 0) {
304 yaml_event_delete(&event);
305 return;
306 }
307 depth--;
308 break;
309 default:
310 error("Syntax error in paring layout.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700311 }
312 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800313 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800314 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800315
Bill Richardson54e95822011-05-05 15:12:10 -0700316 void BmpBlockUtil::parse_screens(yaml_parser_t *parser) {
317 yaml_event_t event;
318 string screen_name;
319 expect_event(parser, YAML_MAPPING_START_EVENT);
320 for (;;) {
321 yaml_parser_parse(parser, &event);
322 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800323 case YAML_SCALAR_EVENT:
324 screen_name = (char*)event.data.scalar.value;
325 config_.screens_map[screen_name] = ScreenConfig();
326 parse_layout(parser, config_.screens_map[screen_name]);
327 break;
328 case YAML_MAPPING_END_EVENT:
329 yaml_event_delete(&event);
330 return;
331 default:
332 error("Syntax error in parsing screens.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700333 }
334 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800335 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800336 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800337
Bill Richardson54e95822011-05-05 15:12:10 -0700338 void BmpBlockUtil::parse_localizations(yaml_parser_t *parser) {
339 yaml_event_t event;
340 int depth = 0, index = 0;
341 expect_event(parser, YAML_SEQUENCE_START_EVENT);
342 for (;;) {
343 yaml_parser_parse(parser, &event);
344 switch (event.type) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800345 case YAML_SEQUENCE_START_EVENT:
346 config_.localizations.push_back(vector<string>());
347 depth++;
348 break;
349 case YAML_SCALAR_EVENT:
350 config_.localizations[index].push_back((char*)event.data.scalar.value);
351 break;
352 case YAML_SEQUENCE_END_EVENT:
353 if (depth == 1) {
354 index++;
355 } else if (depth == 0) {
356 yaml_event_delete(&event);
357 return;
358 }
359 depth--;
360 break;
361 default:
362 error("Syntax error in parsing localizations.\n");
Bill Richardson54e95822011-05-05 15:12:10 -0700363 }
364 yaml_event_delete(&event);
Bill Richardsond55085d2011-02-04 15:01:37 -0800365 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800366 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800367
Bill Richardson8ba3d792011-05-18 18:25:31 -0700368 void BmpBlockUtil::parse_locale_index(yaml_parser_t *parser) {
369 yaml_event_t event;
370 expect_event(parser, YAML_SEQUENCE_START_EVENT);
371 for (;;) {
372 yaml_parser_parse(parser, &event);
373 switch (event.type) {
374 case YAML_SCALAR_EVENT:
375 config_.locale_names.append((char*)event.data.scalar.value);
376 config_.locale_names.append(1, (char)'\0'); // '\0' to delimit
377 break;
378 case YAML_SEQUENCE_END_EVENT:
379 yaml_event_delete(&event);
380 config_.locale_names.append(1, (char)'\0'); // double '\0' to terminate
381 return;
382 default:
383 error("Syntax error in parsing localizations.\n");
384 }
385 }
386 }
387
Bill Richardson54e95822011-05-05 15:12:10 -0700388 void BmpBlockUtil::load_all_image_files() {
389 for (unsigned int i = 0; i < config_.image_names.size(); i++) {
390 StrImageConfigMap::iterator it =
391 config_.images_map.find(config_.image_names[i]);
392 if (debug_) {
393 printf("loading image \"%s\" from \"%s\"\n",
394 config_.image_names[i].c_str(),
395 it->second.filename.c_str());
396 }
397 const string &content = read_image_file(it->second.filename.c_str());
398 it->second.raw_content = content;
399 it->second.data.original_size = content.size();
Bill Richardson0a9977e2011-08-22 16:03:59 -0700400 it->second.data.format =
401 identify_image_type(content.c_str(),
402 (uint32_t)content.size(), &it->second.data);
403 if (FORMAT_INVALID == it->second.data.format) {
Bill Richardson54e95822011-05-05 15:12:10 -0700404 error("Unsupported image format in %s\n", it->second.filename.c_str());
405 }
406 switch(compression_) {
407 case COMPRESS_NONE:
408 it->second.data.compression = compression_;
409 it->second.compressed_content = content;
410 it->second.data.compressed_size = content.size();
411 break;
412 case COMPRESS_EFIv1:
Bill Richardson61362d62011-02-14 10:28:03 -0800413 {
414 // The content will always compress smaller (so sez the docs).
415 uint32_t tmpsize = content.size();
416 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
417 // The size of the compressed content is also returned.
418 if (EFI_SUCCESS != EfiCompress((uint8_t *)content.c_str(), tmpsize,
419 tmpbuf, &tmpsize)) {
420 error("Unable to compress!\n");
421 }
422 it->second.data.compression = compression_;
423 it->second.compressed_content.assign((const char *)tmpbuf, tmpsize);
424 it->second.data.compressed_size = tmpsize;
425 free(tmpbuf);
426 }
427 break;
Bill Richardson54e95822011-05-05 15:12:10 -0700428 case COMPRESS_LZMA1:
Tom Wai-Hong Tamee2bc912011-02-17 12:58:58 +0800429 {
430 // Calculate the worst case of buffer size.
431 uint32_t tmpsize = lzma_stream_buffer_bound(content.size());
432 uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
433 lzma_stream stream = LZMA_STREAM_INIT;
434 lzma_options_lzma options;
435 lzma_ret result;
436
437 lzma_lzma_preset(&options, 9);
438 result = lzma_alone_encoder(&stream, &options);
439 if (result != LZMA_OK) {
440 error("Unable to initialize easy encoder (error: %d)!\n", result);
441 }
442
443 stream.next_in = (uint8_t *)content.data();
444 stream.avail_in = content.size();
445 stream.next_out = tmpbuf;
446 stream.avail_out = tmpsize;
447 result = lzma_code(&stream, LZMA_FINISH);
448 if (result != LZMA_STREAM_END) {
449 error("Unable to encode data (error: %d)!\n", result);
450 }
451
452 it->second.data.compression = compression_;
453 it->second.compressed_content.assign((const char *)tmpbuf,
454 tmpsize - stream.avail_out);
455 it->second.data.compressed_size = tmpsize - stream.avail_out;
456 lzma_end(&stream);
457 free(tmpbuf);
458 }
459 break;
Bill Richardsond55085d2011-02-04 15:01:37 -0800460 default:
Bill Richardson54e95822011-05-05 15:12:10 -0700461 error("Unsupported compression method attempted.\n");
Bill Richardsond55085d2011-02-04 15:01:37 -0800462 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800463 }
464 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800465
Bill Richardson54e95822011-05-05 15:12:10 -0700466 const string BmpBlockUtil::read_image_file(const char *filename) {
467 string content;
468 vector<char> buffer;
Bill Richardsond55085d2011-02-04 15:01:37 -0800469
Bill Richardson54e95822011-05-05 15:12:10 -0700470 FILE *fp = fopen(filename, "rb");
471 if (!fp) {
472 perror(filename);
473 exit(errno);
474 }
475
476 if (fseek(fp, 0, SEEK_END) == 0) {
477 buffer.resize(ftell(fp));
478 rewind(fp);
479 }
480
481 if (!buffer.empty()) {
482 if(fread(&buffer[0], buffer.size(), 1, fp) != 1) {
483 perror(filename);
484 buffer.clear();
485 } else {
486 content.assign(buffer.begin(), buffer.end());
487 }
488 }
489
490 fclose(fp);
491 return content;
492 }
493
Bill Richardson54e95822011-05-05 15:12:10 -0700494 void BmpBlockUtil::fill_bmpblock_header() {
495 memset(&config_.header, '\0', sizeof(config_.header));
496 memcpy(&config_.header.signature, BMPBLOCK_SIGNATURE,
497 BMPBLOCK_SIGNATURE_SIZE);
498 config_.header.major_version = major_version_;
499 config_.header.minor_version = minor_version_;
500 config_.header.number_of_localizations = config_.localizations.size();
501 config_.header.number_of_screenlayouts = config_.localizations[0].size();
Bill Richardson6df3e332014-10-02 18:50:33 -0700502 // NOTE: this is part of the yaml consistency check
Bill Richardson54e95822011-05-05 15:12:10 -0700503 for (unsigned int i = 1; i < config_.localizations.size(); ++i) {
504 assert(config_.header.number_of_screenlayouts ==
505 config_.localizations[i].size());
506 }
507 config_.header.number_of_imageinfos = config_.images_map.size();
Bill Richardson8ba3d792011-05-18 18:25:31 -0700508 config_.header.locale_string_offset = 0; // Filled by pack_bmpblock()
Bill Richardson54e95822011-05-05 15:12:10 -0700509 }
510
511 void BmpBlockUtil::pack_bmpblock() {
512 bmpblock_.clear();
513
514 /* Compute the ImageInfo offsets from start of BMPBLOCK. */
515 uint32_t current_offset = sizeof(BmpBlockHeader) +
516 sizeof(ScreenLayout) * (config_.header.number_of_localizations *
517 config_.header.number_of_screenlayouts);
518 for (StrImageConfigMap::iterator it = config_.images_map.begin();
519 it != config_.images_map.end();
520 ++it) {
521 it->second.offset = current_offset;
522 if (debug_)
Bill Richardson0a9977e2011-08-22 16:03:59 -0700523 printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d fmt=%d\n",
Bill Richardson54e95822011-05-05 15:12:10 -0700524 it->first.c_str(),
525 it->second.filename.c_str(),
526 it->second.offset,
Bill Richardson0a9977e2011-08-22 16:03:59 -0700527 it->second.data.tag,
528 it->second.data.format);
Bill Richardson54e95822011-05-05 15:12:10 -0700529 current_offset += sizeof(ImageInfo) +
530 it->second.data.compressed_size;
531 /* Make it 4-byte aligned. */
532 if ((current_offset & 3) > 0) {
533 current_offset = (current_offset & ~3) + 4;
534 }
535 }
Bill Richardson8ba3d792011-05-18 18:25:31 -0700536 /* And leave room for the locale_index string */
537 if (config_.locale_names.size()) {
538 config_.header.locale_string_offset = current_offset;
539 current_offset += config_.locale_names.size();
540 }
541
Bill Richardson54e95822011-05-05 15:12:10 -0700542 bmpblock_.resize(current_offset);
543
544 /* Fill BmpBlockHeader struct. */
545 string::iterator current_filled = bmpblock_.begin();
546 std::copy(reinterpret_cast<char*>(&config_.header),
547 reinterpret_cast<char*>(&config_.header + 1),
548 current_filled);
549 current_filled += sizeof(config_.header);
550 current_offset = sizeof(config_.header);
551
552 /* Fill all ScreenLayout structs. */
553 for (unsigned int i = 0; i < config_.localizations.size(); ++i) {
554 for (unsigned int j = 0; j < config_.localizations[i].size(); ++j) {
555 ScreenConfig &screen = config_.screens_map[config_.localizations[i][j]];
556 for (unsigned int k = 0;
557 k < MAX_IMAGE_IN_LAYOUT && !screen.image_names[k].empty();
558 ++k) {
559 if (config_.images_map.find(screen.image_names[k]) ==
560 config_.images_map.end()) {
561 error("Invalid image name \"%s\"\n", screen.image_names[k].c_str());
562 }
563 if (debug_)
564 printf("i=%d j=%d k=%d=\"%s\" (%d,%d) ofs=%x\n", i,j,k,
565 screen.image_names[k].c_str(),
566 screen.data.images[k].x, screen.data.images[k].y,
567 config_.images_map[screen.image_names[k]].offset
568 );
569 screen.data.images[k].image_info_offset =
Bill Richardsond55085d2011-02-04 15:01:37 -0800570 config_.images_map[screen.image_names[k]].offset;
Bill Richardson54e95822011-05-05 15:12:10 -0700571 }
572 std::copy(reinterpret_cast<char*>(&screen.data),
573 reinterpret_cast<char*>(&screen.data + 1),
574 current_filled);
575 current_filled += sizeof(screen.data);
576 if (debug_)
577 printf("S: current offset is 0x%08x\n", current_offset);
578 current_offset += sizeof(screen.data);
Bill Richardsond55085d2011-02-04 15:01:37 -0800579 }
Bill Richardson54e95822011-05-05 15:12:10 -0700580 }
581
582 /* Fill all ImageInfo structs and image contents. */
583 for (StrImageConfigMap::iterator it = config_.images_map.begin();
584 it != config_.images_map.end();
585 ++it) {
586 current_filled = bmpblock_.begin() + it->second.offset;
587 current_offset = it->second.offset;
588 if (debug_)
589 printf("I0: current offset is 0x%08x\n", current_offset);
590 std::copy(reinterpret_cast<char*>(&it->second.data),
591 reinterpret_cast<char*>(&it->second.data + 1),
Bill Richardsond55085d2011-02-04 15:01:37 -0800592 current_filled);
Bill Richardson54e95822011-05-05 15:12:10 -0700593 current_filled += sizeof(it->second.data);
594 current_offset += sizeof(it->second.data);
595 if (debug_)
596 printf("I1: current offset is 0x%08x (len %ld)\n",
597 current_offset, it->second.compressed_content.length());
598 std::copy(it->second.compressed_content.begin(),
599 it->second.compressed_content.end(),
600 current_filled);
Bill Richardsond55085d2011-02-04 15:01:37 -0800601 }
Bill Richardson8ba3d792011-05-18 18:25:31 -0700602
603 /* Fill in locale_names. */
604 if (config_.header.locale_string_offset) {
605 current_offset = config_.header.locale_string_offset;
606 current_filled = bmpblock_.begin() + current_offset;
607 if (debug_)
608 printf("locale_names: offset 0x%08x (len %ld)\n",
609 current_offset, config_.locale_names.size());
610 std::copy(config_.locale_names.begin(),
611 config_.locale_names.end(),
612 current_filled);
613 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800614 }
615
Bill Richardson54e95822011-05-05 15:12:10 -0700616 void BmpBlockUtil::write_to_bmpblock(const char *filename) {
617 assert(!bmpblock_.empty());
Bill Richardsond55085d2011-02-04 15:01:37 -0800618
Bill Richardson54e95822011-05-05 15:12:10 -0700619 FILE *fp = fopen(filename, "wb");
620 if (!fp) {
621 perror(filename);
622 exit(errno);
623 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800624
Bill Richardson54e95822011-05-05 15:12:10 -0700625 int r = fwrite(bmpblock_.c_str(), bmpblock_.size(), 1, fp);
626 fclose(fp);
627 if (r != 1) {
628 perror(filename);
629 exit(errno);
630 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800631 }
632
Bill Richardsond55085d2011-02-04 15:01:37 -0800633} // namespace vboot_reference
634
Randall Spangler17f8d342013-01-11 10:55:11 -0800635#ifndef FOR_LIBRARY
Bill Richardsond55085d2011-02-04 15:01:37 -0800636
Bill Richardson54e95822011-05-05 15:12:10 -0700637 //////////////////////////////////////////////////////////////////////////////
638 // Command line utilities.
Bill Richardson794d4d42011-02-10 19:13:10 -0800639
Bill Richardson54e95822011-05-05 15:12:10 -0700640 extern "C" {
Bill Richardson794d4d42011-02-10 19:13:10 -0800641#include "bmpblk_util.h"
Bill Richardson54e95822011-05-05 15:12:10 -0700642 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800643
Bill Richardson54e95822011-05-05 15:12:10 -0700644 using vboot_reference::BmpBlockUtil;
Bill Richardsond55085d2011-02-04 15:01:37 -0800645
Bill Richardson54e95822011-05-05 15:12:10 -0700646 // utility function: provide usage of this utility and exit.
647 static void usagehelp_exit(const char *prog_name) {
648 printf(
649 "\n"
650 "To create a new BMPBLOCK file using config from YAML file:\n"
651 "\n"
652 " %s [-z NUM] -c YAML BMPBLOCK\n"
653 "\n"
654 " -z NUM = compression algorithm to use\n"
655 " 0 = none\n"
656 " 1 = EFIv1\n"
657 " 2 = LZMA1\n"
658 "\n", prog_name);
659 printf(
660 "To display the contents of a BMPBLOCK:\n"
661 "\n"
662 " %s [-y] BMPBLOCK\n"
663 "\n"
664 " -y = display as yaml\n"
665 "\n", prog_name);
666 printf(
667 "To unpack a BMPBLOCK file:\n"
668 "\n"
669 " %s -x [-d DIR] [-f] BMPBLOCK\n"
670 "\n"
671 " -d DIR = directory to use (default '.')\n"
672 " -f = force overwriting existing files\n"
673 "\n", prog_name);
674 exit(1);
675 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800676
Bill Richardson54e95822011-05-05 15:12:10 -0700677 ///////////////////////////////////////////////////////////////////////
678 // main
Bill Richardsond55085d2011-02-04 15:01:37 -0800679
Bill Richardson54e95822011-05-05 15:12:10 -0700680 int main(int argc, char *argv[]) {
Bill Richardsond55085d2011-02-04 15:01:37 -0800681
Bill Richardson54e95822011-05-05 15:12:10 -0700682 const char *prog_name = strrchr(argv[0], '/');
683 if (prog_name)
684 prog_name++;
685 else
686 prog_name = argv[0];
Bill Richardsond55085d2011-02-04 15:01:37 -0800687
Bill Richardson54e95822011-05-05 15:12:10 -0700688 int overwrite = 0, extract_mode = 0;
689 int compression = 0;
690 int set_compression = 0;
691 const char *config_fn = 0, *bmpblock_fn = 0, *extract_dir = ".";
692 int show_as_yaml = 0;
693 bool debug = false;
Bill Richardsond55085d2011-02-04 15:01:37 -0800694
Bill Richardson54e95822011-05-05 15:12:10 -0700695 int opt;
696 opterr = 0; // quiet
697 int errorcnt = 0;
698 char *e = 0;
699 while ((opt = getopt(argc, argv, ":c:xz:fd:yD")) != -1) {
700 switch (opt) {
701 case 'c':
702 config_fn = optarg;
703 break;
704 case 'x':
705 extract_mode = 1;
706 break;
707 case 'y':
708 show_as_yaml = 1;
709 break;
710 case 'z':
711 compression = (int)strtoul(optarg, &e, 0);
712 if (!*optarg || (e && *e)) {
713 fprintf(stderr, "%s: invalid argument to -%c: \"%s\"\n",
714 prog_name, opt, optarg);
715 errorcnt++;
716 }
717 if (compression >= MAX_COMPRESS) {
718 fprintf(stderr, "%s: compression type must be less than %d\n",
719 prog_name, MAX_COMPRESS);
720 errorcnt++;
721 }
722 set_compression = 1;
723 break;
724 case 'f':
725 overwrite = 1;
726 break;
727 case 'd':
728 extract_dir= optarg;
729 break;
730 case 'D':
731 debug = true;
732 break;
733 case ':':
734 fprintf(stderr, "%s: missing argument to -%c\n",
735 prog_name, optopt);
Bill Richardson794d4d42011-02-10 19:13:10 -0800736 errorcnt++;
Bill Richardson54e95822011-05-05 15:12:10 -0700737 break;
738 default:
739 fprintf(stderr, "%s: unrecognized switch: -%c\n",
740 prog_name, optopt);
Bill Richardson794d4d42011-02-10 19:13:10 -0800741 errorcnt++;
Bill Richardson54e95822011-05-05 15:12:10 -0700742 break;
Bill Richardson794d4d42011-02-10 19:13:10 -0800743 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800744 }
Bill Richardson54e95822011-05-05 15:12:10 -0700745 argc -= optind;
746 argv += optind;
747
748 if (argc >= 1) {
749 bmpblock_fn = argv[0];
750 } else {
751 fprintf(stderr, "%s: missing BMPBLOCK name\n", prog_name);
752 errorcnt++;
753 }
754
755 if (errorcnt)
756 usagehelp_exit(prog_name);
757
758 BmpBlockUtil util(debug);
759
760 if (config_fn) {
761 if (set_compression)
762 util.force_compression(compression);
763 util.load_from_config(config_fn);
764 util.pack_bmpblock();
765 util.write_to_bmpblock(bmpblock_fn);
766 }
767
768 else if (extract_mode) {
769 return dump_bmpblock(bmpblock_fn, 1, extract_dir, overwrite);
770 } else {
771 return dump_bmpblock(bmpblock_fn, show_as_yaml, 0, 0);
772 }
773
774 return 0;
Bill Richardsond55085d2011-02-04 15:01:37 -0800775 }
Bill Richardsond55085d2011-02-04 15:01:37 -0800776
Randall Spangler17f8d342013-01-11 10:55:11 -0800777#endif // FOR_LIBRARY