bmpblk_util.c 13 KB

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