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 #include <errno.h>
6 #include <fcntl.h>
7 #include <limits.h>
8 #include <lzma.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <stdio.h>
12 #include <string.h>
13 #include <sys/mman.h>
14 #include <sys/stat.h>
15 #include <sys/types.h>
16 #include <unistd.h>
17
18 #include "bmpblk_util.h"
19 #include "eficompress.h"
20 #include "vboot_api.h"
21
22 // Returns pointer to buffer containing entire file, sets length.
read_entire_file(const char * filename,size_t * length)23 static void *read_entire_file(const char *filename, size_t *length) {
24 int fd;
25 struct stat sbuf;
26 void *ptr;
27
28 *length = 0; // just in case
29
30 if (0 != stat(filename, &sbuf)) {
31 fprintf(stderr, "Unable to stat %s: %s\n", filename, strerror(errno));
32 return 0;
33 }
34
35 if (!sbuf.st_size) {
36 fprintf(stderr, "File %s is empty\n", filename);
37 return 0;
38 }
39
40 fd = open(filename, O_RDONLY);
41 if (fd < 0) {
42 fprintf(stderr, "Unable to open %s: %s\n", filename, strerror(errno));
43 return 0;
44 }
45
46 ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
47 if (MAP_FAILED == ptr) {
48 fprintf(stderr, "Unable to mmap %s: %s\n", filename, strerror(errno));
49 close(fd);
50 return 0;
51 }
52
53 *length = sbuf.st_size;
54
55 close(fd);
56
57 return ptr;
58 }
59
60
61 // Reclaims buffer from read_entire_file().
discard_file(void * ptr,size_t length)62 static void discard_file(void *ptr, size_t length) {
63 munmap(ptr, length);
64 }
65
66 //////////////////////////////////////////////////////////////////////////////
67
require_dir(const char * dirname)68 static int require_dir(const char *dirname) {
69 struct stat sbuf;
70
71 if (0 == stat(dirname, &sbuf)) {
72 // Something's there. Is it a directory?
73 if (S_ISDIR(sbuf.st_mode)) {
74 return 0;
75 }
76 fprintf(stderr, "%s already exists and is not a directory\n", dirname);
77 return 1;
78 }
79
80 // dirname doesn't exist. Try to create it.
81 if (ENOENT == errno) {
82 if (0 != mkdir(dirname, 0777)) {
83 fprintf(stderr, "Unable to create directory %s: %s\n",
84 dirname, strerror(errno));
85 return 1;
86 }
87 return 0;
88 }
89
90 fprintf(stderr, "Unable to stat %s: %s\n", dirname, strerror(errno));
91 return 1;
92 }
93
94
95
do_efi_decompress(ImageInfo * img)96 static void *do_efi_decompress(ImageInfo *img) {
97 void *ibuf;
98 void *sbuf;
99 void *obuf;
100 uint32_t isize;
101 uint32_t ssize;
102 uint32_t osize;
103 EFI_STATUS r;
104
105 ibuf = (void*)(img + 1);
106 isize = img->compressed_size;
107
108 r = EfiGetInfo(ibuf, isize, &osize, &ssize);
109 if (EFI_SUCCESS != r) {
110 fprintf(stderr, "EfiGetInfo() failed with code %d\n",
111 r);
112 return 0;
113 }
114
115 sbuf = malloc(ssize);
116 if (!sbuf) {
117 fprintf(stderr, "Can't allocate %d bytes: %s\n",
118 ssize,
119 strerror(errno));
120 return 0;
121 }
122
123 obuf = malloc(osize);
124 if (!obuf) {
125 fprintf(stderr, "Can't allocate %d bytes: %s\n",
126 osize,
127 strerror(errno));
128 free(sbuf);
129 return 0;
130 }
131
132 r = EfiDecompress(ibuf, isize, obuf, osize, sbuf, ssize);
133 if (r != EFI_SUCCESS) {
134 fprintf(stderr, "EfiDecompress failed with code %d\n", r);
135 free(obuf);
136 free(sbuf);
137 return 0;
138 }
139
140 free(sbuf);
141 return obuf;
142 }
143
144
145
do_lzma_decompress(ImageInfo * img)146 static void *do_lzma_decompress(ImageInfo *img) {
147 void *ibuf;
148 void *obuf;
149 uint32_t isize;
150 uint32_t osize;
151 lzma_stream stream = LZMA_STREAM_INIT;
152 lzma_ret result;
153
154 ibuf = (void*)(img + 1);
155 isize = img->compressed_size;
156 osize = img->original_size;
157 obuf = malloc(osize);
158 if (!obuf) {
159 fprintf(stderr, "Can't allocate %d bytes: %s\n",
160 osize,
161 strerror(errno));
162 return 0;
163 }
164
165 result = lzma_auto_decoder(&stream, -1, 0);
166 if (result != LZMA_OK) {
167 fprintf(stderr, "Unable to initialize auto decoder (error: %d)!\n",
168 result);
169 free(obuf);
170 return 0;
171 }
172
173 stream.next_in = ibuf;
174 stream.avail_in = isize;
175 stream.next_out = obuf;
176 stream.avail_out = osize;
177 result = lzma_code(&stream, LZMA_FINISH);
178 if (result != LZMA_STREAM_END) {
179 fprintf(stderr, "Unalbe to decode data (error: %d)!\n", result);
180 free(obuf);
181 return 0;
182 }
183 lzma_end(&stream);
184 return obuf;
185 }
186
187
188
189 // Show what's inside. If todir is NULL, just print. Otherwise unpack.
dump_bmpblock(const char * infile,int show_as_yaml,const char * todir,int overwrite)190 int dump_bmpblock(const char *infile, int show_as_yaml,
191 const char *todir, int overwrite) {
192 void *ptr, *data_ptr;
193 size_t length = 0;
194 BmpBlockHeader *hdr;
195 ImageInfo *img;
196 ScreenLayout *scr;
197 int loc_num;
198 int screen_num;
199 int i;
200 int offset;
201 int free_data;
202 char image_name[80];
203 char full_path_name[PATH_MAX];
204 int yfd, bfd;
205 FILE *yfp = stdout;
206 FILE *bfp = stdout;
207
208 ptr = (void *)read_entire_file(infile, &length);
209 if (!ptr)
210 return 1;
211
212 if (length < sizeof(BmpBlockHeader)) {
213 fprintf(stderr, "File %s is too small to be a BMPBLOCK\n", infile);
214 discard_file(ptr, length);
215 return 1;
216 }
217
218 if (0 != memcmp(ptr, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE)) {
219 fprintf(stderr, "File %s is not a BMPBLOCK\n", infile);
220 discard_file(ptr, length);
221 return 1;
222 }
223
224 if (todir) {
225 // Unpacking everything. Create the output directory if needed.
226 if (0 != require_dir(todir)) {
227 discard_file(ptr, length);
228 return 1;
229 }
230
231 // Open yaml output.
232 show_as_yaml = 1;
233
234 sprintf(full_path_name, "%s/%s", todir, "config.yaml");
235 yfd = open(full_path_name,
236 O_WRONLY | O_CREAT | O_TRUNC | (overwrite ? 0 : O_EXCL),
237 0666);
238 if (yfd < 0) {
239 fprintf(stderr, "Unable to open %s: %s\n", full_path_name,
240 strerror(errno));
241 discard_file(ptr, length);
242 return 1;
243 }
244
245 yfp = fdopen(yfd, "wb");
246 if (!yfp) {
247 fprintf(stderr, "Unable to fdopen %s: %s\n", full_path_name,
248 strerror(errno));
249 close(yfd);
250 discard_file(ptr, length);
251 return 1;
252 }
253 }
254
255 hdr = (BmpBlockHeader *)ptr;
256
257 if (!show_as_yaml) {
258 printf("%s:\n", infile);
259 printf(" version %d.%d\n", hdr->major_version, hdr->minor_version);
260 printf(" %d screens\n", hdr->number_of_screenlayouts);
261 printf(" %d localizations\n", hdr->number_of_localizations);
262 printf(" %d discrete images\n", hdr->number_of_imageinfos);
263 discard_file(ptr, length);
264 return 0;
265 }
266
267 // Write out yaml
268 fprintf(yfp, "bmpblock: %d.%d\n", hdr->major_version, hdr->minor_version);
269 offset = sizeof(BmpBlockHeader) +
270 (sizeof(ScreenLayout) *
271 hdr->number_of_localizations *
272 hdr->number_of_screenlayouts);
273 // FIXME(chromium-os:12134): The bmbblock structure allows each image to be
274 // compressed differently, but we haven't provided a way for the yaml file to
275 // specify that. Additionally, we allow the yaml file to specify a default
276 // compression scheme for all images, but only if that line appears in the
277 // yaml file before any images. Accordingly, we'll just check the first image
278 // to see if it has any compression, and if it does, we'll write that out as
279 // the default. When this bug is fixed, we should just write each image's
280 // compression setting separately.
281 img = (ImageInfo *)(ptr + offset);
282 if (img->compression)
283 fprintf(yfp, "compression: %d\n", img->compression);
284 fprintf(yfp, "images:\n");
285 for(i=0; i<hdr->number_of_imageinfos; i++) {
286 img = (ImageInfo *)(ptr + offset);
287 if (img->compressed_size) {
288 sprintf(image_name, "img_%08x.bmp", offset);
289 if (img->tag == TAG_HWID) {
290 fprintf(yfp, " %s: %s # %dx%d %d/%d tag=%d fmt=%d\n",
291 RENDER_HWID, image_name,
292 img->width, img->height,
293 img->compressed_size, img->original_size,
294 img->tag, img->format);
295 } else if (img->tag == TAG_HWID_RTOL) {
296 fprintf(yfp, " %s: %s # %dx%d %d/%d tag=%d fmt=%d\n",
297 RENDER_HWID_RTOL, image_name,
298 img->width, img->height,
299 img->compressed_size, img->original_size,
300 img->tag, img->format);
301 } else {
302 fprintf(yfp, " img_%08x: %s # %dx%d %d/%d tag=%d fmt=%d\n",
303 offset, image_name,
304 img->width, img->height,
305 img->compressed_size, img->original_size,
306 img->tag, img->format);
307 }
308 if (todir) {
309 sprintf(full_path_name, "%s/%s", todir, image_name);
310 bfd = open(full_path_name,
311 O_WRONLY | O_CREAT | O_TRUNC | (overwrite ? 0 : O_EXCL),
312 0666);
313 if (bfd < 0) {
314 fprintf(stderr, "Unable to open %s: %s\n", full_path_name,
315 strerror(errno));
316 fclose(yfp);
317 discard_file(ptr, length);
318 return 1;
319 }
320 bfp = fdopen(bfd, "wb");
321 if (!bfp) {
322 fprintf(stderr, "Unable to fdopen %s: %s\n", full_path_name,
323 strerror(errno));
324 close(bfd);
325 fclose(yfp);
326 discard_file(ptr, length);
327 return 1;
328 }
329 switch(img->compression) {
330 case COMPRESS_NONE:
331 data_ptr = ptr + offset + sizeof(ImageInfo);
332 free_data = 0;
333 break;
334 case COMPRESS_EFIv1:
335 data_ptr = do_efi_decompress(img);
336 if (!data_ptr) {
337 fclose(bfp);
338 fclose(yfp);
339 discard_file(ptr, length);
340 return 1;
341 }
342 free_data = 1;
343 break;
344 case COMPRESS_LZMA1:
345 data_ptr = do_lzma_decompress(img);
346 if (!data_ptr) {
347 fclose(bfp);
348 fclose(yfp);
349 discard_file(ptr, length);
350 return 1;
351 }
352 free_data = 1;
353 break;
354 default:
355 fprintf(stderr, "Unsupported compression method encountered.\n");
356 fclose(bfp);
357 fclose(yfp);
358 discard_file(ptr, length);
359 return 1;
360 }
361 if (1 != fwrite(data_ptr, img->original_size, 1, bfp)) {
362 fprintf(stderr, "Unable to write %s: %s\n", full_path_name,
363 strerror(errno));
364 fclose(bfp);
365 fclose(yfp);
366 discard_file(ptr, length);
367 return 1;
368 }
369 fclose(bfp);
370 if (free_data)
371 free(data_ptr);
372 }
373 }
374 offset += sizeof(ImageInfo);
375 offset += img->compressed_size;
376 // 4-byte aligned
377 if ((offset & 3) > 0)
378 offset = (offset & ~3) + 4;
379 }
380 fprintf(yfp, "screens:\n");
381 for(loc_num = 0;
382 loc_num < hdr->number_of_localizations;
383 loc_num++) {
384 for(screen_num = 0;
385 screen_num < hdr->number_of_screenlayouts;
386 screen_num++) {
387 fprintf(yfp, " scr_%d_%d:\n", loc_num, screen_num);
388 i = loc_num * hdr->number_of_screenlayouts + screen_num;
389 offset = sizeof(BmpBlockHeader) + i * sizeof(ScreenLayout);
390 scr = (ScreenLayout *)(ptr + offset);
391 for(i=0; i<MAX_IMAGE_IN_LAYOUT; i++) {
392 if (scr->images[i].image_info_offset) {
393 ImageInfo *iptr =
394 (ImageInfo *)(ptr + scr->images[i].image_info_offset);
395 if (iptr->tag == TAG_HWID) {
396 fprintf(yfp, " - [%d, %d, %s] # tag=%d fmt=%d c=%d %d/%d\n",
397 scr->images[i].x, scr->images[i].y,
398 RENDER_HWID, iptr->tag, iptr->format, iptr->compression,
399 iptr->compressed_size, iptr->original_size);
400 } else if (iptr->tag == TAG_HWID_RTOL) {
401 fprintf(yfp, " - [%d, %d, %s] # tag=%d fmt=%d c=%d %d/%d\n",
402 scr->images[i].x, scr->images[i].y,
403 RENDER_HWID_RTOL, iptr->tag,
404 iptr->format, iptr->compression,
405 iptr->compressed_size, iptr->original_size);
406 } else {
407 fprintf(yfp, " - [%d, %d, img_%08x]"
408 " # tag=%d fmt=%d c=%d %d/%d\n",
409 scr->images[i].x, scr->images[i].y,
410 scr->images[i].image_info_offset,
411 iptr->tag, iptr->format, iptr->compression,
412 iptr->compressed_size, iptr->original_size);
413 }
414 }
415 }
416 }
417 }
418 fprintf(yfp, "localizations:\n");
419 for(loc_num = 0;
420 loc_num < hdr->number_of_localizations;
421 loc_num++) {
422 fprintf(yfp, " - [");
423 for(screen_num = 0;
424 screen_num < hdr->number_of_screenlayouts;
425 screen_num++) {
426 fprintf(yfp, " scr_%d_%d", loc_num, screen_num);
427 if (screen_num != hdr->number_of_screenlayouts - 1)
428 fprintf(yfp, ",");
429 }
430 fprintf(yfp, " ]\n");
431 }
432
433 if (hdr->locale_string_offset) {
434 char *loc_ptr = (char *)ptr + hdr->locale_string_offset;
435 char c;
436 fprintf(yfp, "locale_index:\n");
437 while ((c = *loc_ptr) != '\0') {
438 fprintf(yfp, " - ");
439 do {
440 fputc(c, yfp);
441 loc_ptr++;
442 } while((c = *loc_ptr) != '\0');
443 loc_ptr++;
444 fputc('\n', yfp);
445 }
446 }
447
448 if (todir)
449 fclose(yfp);
450
451 discard_file(ptr, length);
452
453 return 0;
454 }
455
456