Enable LZMA compression in bmpbklk_utility.

LZMA has better compression ratio and is also supported in u-boot already.
ARM BIOS will use LZMA to compress BMP files.

BUG=chromium-os:11017
TEST=manual
$ make
$ make runbmptests

Change-Id: I6b791e3284b65eb3085b0de548bd241eab2ee598

Review URL: http://codereview.chromium.org/6523019
diff --git a/firmware/include/bmpblk_header.h b/firmware/include/bmpblk_header.h
index 0d67a49..0c609dd 100644
--- a/firmware/include/bmpblk_header.h
+++ b/firmware/include/bmpblk_header.h
@@ -117,7 +117,7 @@
 typedef enum Compression {
   COMPRESS_NONE = 0,
   COMPRESS_EFIv1,           /* The x86 BIOS only supports this */
-  COMPRESS_TBD,             /* Only on ARM? */
+  COMPRESS_LZMA1,           /* The ARM BIOS supports LZMA1 */
   MAX_COMPRESS,
 } Compression;
 
diff --git a/tests/bitmaps/TestBmpBlock.py b/tests/bitmaps/TestBmpBlock.py
index ced270e..be81592 100755
--- a/tests/bitmaps/TestBmpBlock.py
+++ b/tests/bitmaps/TestBmpBlock.py
@@ -89,19 +89,27 @@
     self.assertEqual(0, rc)
     os.chdir('..')
 
-  def testPackUnpackZ(self):
-    """Create, unpack, recreate with explicit compression"""
-    rc, out, err = runprog(prog, '-z', '1', '-c', 'case_simple.yaml', 'FOO')
+  def doPackUnpackZ(self, comp):
+    """Create, unpack, recreate with a given compression"""
+    rc, out, err = runprog(prog, '-z', comp, '-c', 'case_simple.yaml', 'FOO')
     self.assertEqual(0, rc)
     rc, out, err = runprog(prog, '-x', '-d', './FOO_DIR', 'FOO')
     self.assertEqual(0, rc)
     os.chdir('./FOO_DIR')
-    rc, out, err = runprog(prog, '-z', '1', '-c', 'config.yaml', 'BAR')
+    rc, out, err = runprog(prog, '-z', comp, '-c', 'config.yaml', 'BAR')
     self.assertEqual(0, rc)
     rc, out, err = runprog('/usr/bin/cmp', '../FOO', 'BAR')
     self.assertEqual(0, rc)
     os.chdir('..')
 
+  def testPackUnpackZ1(self):
+    """Create, unpack, recreate with EFIv1 compression"""
+    self.doPackUnpackZ('1');
+
+  def testPackUnpackZ2(self):
+    """Create, unpack, recreate with LZMA compression"""
+    self.doPackUnpackZ('2');
+
   def tearDown(self):
     rc, out, err = runprog('/bin/rm', '-rf', './FOO_DIR', 'FOO')
     self.assertEqual(0, rc)
diff --git a/utility/Makefile b/utility/Makefile
index af0187b..8ea53a6 100644
--- a/utility/Makefile
+++ b/utility/Makefile
@@ -60,7 +60,7 @@
 	$(CXX) -DWITH_UTIL_MAIN $(CFLAGS) $< -o $@
 
 ${BUILD_ROOT}/bmpblk_utility.o: bmpblk_utility.cc
-	$(CXX) -DWITH_UTIL_MAIN -lyaml $(CFLAGS) -c $< -o $@
+	$(CXX) -DWITH_UTIL_MAIN $(CFLAGS) -c $< -o $@
 
 ${BUILD_ROOT}/bmpblk_util.o: bmpblk_util.c
 	$(CC) $(CFLAGS) -c $< -o $@
@@ -75,7 +75,7 @@
 				${BUILD_ROOT}/bmpblk_util.o \
 				${BUILD_ROOT}/eficompress.o \
 				${BUILD_ROOT}/efidecompress.o
-	$(CXX) -DWITH_UTIL_MAIN -lyaml $(CFLAGS) $^ -o $@
+	$(CXX) -llzma -lyaml $(CFLAGS) $^ -o $@
 
 ${BUILD_ROOT}/load_kernel_test: load_kernel_test.c $(LIBS)
 	$(CC) $(CFLAGS) $(INCLUDES) $< -o $@ $(LIBS) -lcrypto
diff --git a/utility/bmpblk_util.c b/utility/bmpblk_util.c
index 214beda..f2b0adc 100644
--- a/utility/bmpblk_util.c
+++ b/utility/bmpblk_util.c
@@ -5,6 +5,7 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <lzma.h>
 #include <stdio.h>
 #include <string.h>
 #include <sys/mman.h>
@@ -99,7 +100,7 @@
   uint32_t osize;
   EFI_STATUS r;
 
-  ibuf = ((void *)img) + sizeof(ImageInfo);
+  ibuf = (void*)(img + 1);
   isize = img->compressed_size;
 
   r = EfiGetInfo(ibuf, isize, &osize, &ssize);
@@ -139,6 +140,50 @@
 }
 
 
+
+static void *do_lzma_decompress(ImageInfo *img) {
+  void *ibuf;
+  void *obuf;
+  uint32_t isize;
+  uint32_t osize;
+  lzma_stream stream = LZMA_STREAM_INIT;
+  lzma_ret result;
+
+  ibuf = (void*)(img + 1);
+  isize = img->compressed_size;
+  osize = img->original_size;
+  obuf = malloc(osize);
+  if (!obuf) {
+    fprintf(stderr, "Can't allocate %d bytes: %s\n",
+            osize,
+            strerror(errno));
+    return 0;
+  }
+
+  result = lzma_auto_decoder(&stream, -1, 0);
+  if (result != LZMA_OK) {
+    fprintf(stderr, "Unable to initialize auto decoder (error: %d)!\n",
+            result);
+    free(obuf);
+    return 0;
+  }
+
+  stream.next_in = ibuf;
+  stream.avail_in = isize;
+  stream.next_out = obuf;
+  stream.avail_out = osize;
+  result = lzma_code(&stream, LZMA_FINISH);
+  if (result != LZMA_STREAM_END) {
+    fprintf(stderr, "Unalbe to decode data (error: %d)!\n", result);
+    free(obuf);
+    return 0;
+  }
+  lzma_end(&stream);
+  return obuf;
+}
+
+
+
 // Show what's inside. If todir is NULL, just print. Otherwise unpack.
 int dump_bmpblock(const char *infile, int show_as_yaml,
                   const char *todir, int overwrite) {
@@ -266,6 +311,16 @@
         }
         free_data = 1;
         break;
+      case COMPRESS_LZMA1:
+        data_ptr = do_lzma_decompress(img);
+        if (!data_ptr) {
+          fclose(bfp);
+          fclose(yfp);
+          discard_file(ptr, length);
+          return 1;
+        }
+        free_data = 1;
+        break;
       default:
         fprintf(stderr, "Unsupported compression method encountered.\n");
         fclose(bfp);
diff --git a/utility/bmpblk_utility.cc b/utility/bmpblk_utility.cc
index 9f912d0..c9188a8 100644
--- a/utility/bmpblk_utility.cc
+++ b/utility/bmpblk_utility.cc
@@ -10,6 +10,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <getopt.h>
+#include <lzma.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -326,6 +327,38 @@
         free(tmpbuf);
       }
       break;
+    case COMPRESS_LZMA1:
+      {
+        // Calculate the worst case of buffer size.
+        uint32_t tmpsize = lzma_stream_buffer_bound(content.size());
+        uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
+        lzma_stream stream = LZMA_STREAM_INIT;
+        lzma_options_lzma options;
+        lzma_ret result;
+
+        lzma_lzma_preset(&options, 9);
+        result = lzma_alone_encoder(&stream, &options);
+        if (result != LZMA_OK) {
+          error("Unable to initialize easy encoder (error: %d)!\n", result);
+        }
+
+        stream.next_in = (uint8_t *)content.data();
+        stream.avail_in = content.size();
+        stream.next_out = tmpbuf;
+        stream.avail_out = tmpsize;
+        result = lzma_code(&stream, LZMA_FINISH);
+        if (result != LZMA_STREAM_END) {
+          error("Unable to encode data (error: %d)!\n", result);
+        }
+
+        it->second.data.compression = compression_;
+        it->second.compressed_content.assign((const char *)tmpbuf,
+                                             tmpsize - stream.avail_out);
+        it->second.data.compressed_size = tmpsize - stream.avail_out;
+        lzma_end(&stream);
+        free(tmpbuf);
+      }
+      break;
     default:
       error("Unsupported compression method attempted.\n");
     }
@@ -535,7 +568,7 @@
     "    -z NUM  = compression algorithm to use\n"
     "              0 = none\n"
     "              1 = EFIv1\n"
-    "              2 = TBD\n"
+    "              2 = LZMA1\n"
     "\n", prog_name);
   printf(
     "To display the contents of a BMPBLOCK:\n"