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. 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(). 62 static void discard_file(void *ptr, size_t length) { 63 munmap(ptr, length); 64 } 65 66 ////////////////////////////////////////////////////////////////////////////// 67 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 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 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. 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