Enable EFIv1 compression in bmpbklk_utility.

This lets bmpbklk_utility generate BMPBLOCKs with EFIv1-compressed bitmaps.
It also adds the ability to display or unpack BMPBLOCK blobs.

The compression/decompression routines come from the tianocore EDK on
sourceforge and are written in C, so now there's a mix of C and C++, but it
works just fine.

BUG=chromium-os:11491
TEST=manual

cd src/platform/vboot_reference
make
make runbmptests

Review URL: http://codereview.chromium.org/6508006

Change-Id: Ie05e1a3fd42f4694447c8c440b2432af4ac0f601
This commit is contained in:
Bill Richardson
2011-02-14 10:28:03 -08:00
parent a985ed405e
commit 61362d65fc
12 changed files with 3121 additions and 44 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -19,7 +19,7 @@ def runprog(*args):
return (p.returncode, out, err)
class TestBmpBlock(unittest.TestCase):
class TestFailures(unittest.TestCase):
def testNoArgs(self):
"""Running with no args should print usage and fail."""
@@ -40,6 +40,72 @@ class TestBmpBlock(unittest.TestCase):
self.assertNotEqual(0, rc)
self.assertTrue(err.count("Unsupported image format"))
def testBadCompression(self):
"""Wrong compression types should fail."""
rc, out, err = runprog(prog, '-z', '99', '-c', 'case_simple.yaml', 'FOO')
self.assertNotEqual(0, rc)
self.assertTrue(err.count("compression type"))
class TestOverWrite(unittest.TestCase):
def setUp(self):
rc, out, err = runprog('/bin/rm', '-rf', './FOO_DIR', 'FOO')
self.assertEqual(0, rc)
def testOverwrite(self):
"""Create, unpack, unpack again, with and without -f"""
rc, out, err = runprog(prog, '-c', 'case_simple.yaml', 'FOO')
self.assertEqual(0, rc)
rc, out, err = runprog(prog, '-x', '-d', './FOO_DIR', 'FOO')
self.assertEqual(0, rc)
rc, out, err = runprog(prog, '-x', '-d', './FOO_DIR', 'FOO')
self.assertNotEqual(0, rc)
self.assertTrue(err.count("File exists"))
rc, out, err = runprog(prog, '-x', '-d', './FOO_DIR', '-f', 'FOO')
self.assertEqual(0, rc)
def tearDown(self):
rc, out, err = runprog('/bin/rm', '-rf', './FOO_DIR', 'FOO')
self.assertEqual(0, rc)
class TestPackUnpack(unittest.TestCase):
def setUp(self):
rc, out, err = runprog('/bin/rm', '-rf', './FOO_DIR', 'FOO')
self.assertEqual(0, rc)
def testPackUnpack(self):
"""Create, unpack, recreate without compression"""
rc, out, err = runprog(prog, '-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, '-c', 'config.yaml', 'BAR')
self.assertEqual(0, rc)
rc, out, err = runprog('/usr/bin/cmp', '../FOO', 'BAR')
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')
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')
self.assertEqual(0, rc)
rc, out, err = runprog('/usr/bin/cmp', '../FOO', 'BAR')
self.assertEqual(0, rc)
os.chdir('..')
def tearDown(self):
rc, out, err = runprog('/bin/rm', '-rf', './FOO_DIR', 'FOO')
self.assertEqual(0, rc)
# Run these tests
if __name__ == '__main__':

BIN
tests/bitmaps/Word.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@@ -0,0 +1,27 @@
bmpblock: 1.0
images:
background: Background.bmp
text: Word.bmp
screens:
scr_1:
- [0, 0, background]
- [45, 45, text ]
scr_2:
- [0, 0, background]
- [45, 400, text ]
scr_3:
- [0, 0, background]
- [400, 400, text ]
scr_4:
- [0, 0, background]
- [400, 45, text ]
localizations:
- [ scr_1, scr_2, scr_3, scr_4 ]

View File

@@ -65,8 +65,16 @@ ${BUILD_ROOT}/bmpblk_utility.o: bmpblk_utility.cc
${BUILD_ROOT}/bmpblk_util.o: bmpblk_util.c
$(CC) $(CFLAGS) -c $< -o $@
${BUILD_ROOT}/eficompress.o: eficompress.c
$(CC) $(CFLAGS) -c $< -o $@
${BUILD_ROOT}/efidecompress.o: efidecompress.c
$(CC) $(CFLAGS) -c $< -o $@
${BUILD_ROOT}/bmpblk_utility: ${BUILD_ROOT}/bmpblk_utility.o \
${BUILD_ROOT}/bmpblk_util.o
${BUILD_ROOT}/bmpblk_util.o \
${BUILD_ROOT}/eficompress.o \
${BUILD_ROOT}/efidecompress.o
$(CXX) -DWITH_UTIL_MAIN -lyaml $(CFLAGS) $^ -o $@
${BUILD_ROOT}/load_kernel_test: load_kernel_test.c $(LIBS)

View File

@@ -4,6 +4,7 @@
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
@@ -12,6 +13,7 @@
#include <unistd.h>
#include "bmpblk_util.h"
#include "eficompress.h"
// Returns pointer to buffer containing entire file, sets length.
@@ -58,14 +60,105 @@ static void discard_file(void *ptr, size_t length) {
munmap(ptr, length);
}
//////////////////////////////////////////////////////////////////////////////
// Show what's inside
int display_bmpblock(const char *infile) {
char *ptr;
static int require_dir(const char *dirname) {
struct stat sbuf;
if (0 == stat(dirname, &sbuf)) {
// Something's there. Is it a directory?
if (S_ISDIR(sbuf.st_mode)) {
return 0;
}
fprintf(stderr, "%s already exists and is not a directory\n", dirname);
return 1;
}
// dirname doesn't exist. Try to create it.
if (ENOENT == errno) {
if (0 != mkdir(dirname, 0777)) {
fprintf(stderr, "Unable to create directory %s: %s\n",
dirname, strerror(errno));
return 1;
}
return 0;
}
fprintf(stderr, "Unable to stat %s: %s\n", dirname, strerror(errno));
return 1;
}
static void *do_efi_decompress(ImageInfo *img) {
void *ibuf;
void *sbuf;
void *obuf;
uint32_t isize;
uint32_t ssize;
uint32_t osize;
EFI_STATUS r;
ibuf = ((void *)img) + sizeof(ImageInfo);
isize = img->compressed_size;
r = EfiGetInfo(ibuf, isize, &osize, &ssize);
if (EFI_SUCCESS != r) {
fprintf(stderr, "EfiGetInfo() failed with code %d\n",
r);
return 0;
}
sbuf = malloc(ssize);
if (!sbuf) {
fprintf(stderr, "Can't allocate %d bytes: %s\n",
ssize,
strerror(errno));
return 0;
}
obuf = malloc(osize);
if (!obuf) {
fprintf(stderr, "Can't allocate %d bytes: %s\n",
osize,
strerror(errno));
free(sbuf);
return 0;
}
r = EfiDecompress(ibuf, isize, obuf, osize, sbuf, ssize);
if (r != EFI_SUCCESS) {
fprintf(stderr, "EfiDecompress failed with code %d\n", r);
free(obuf);
free(sbuf);
return 0;
}
free(sbuf);
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) {
void *ptr, *data_ptr;
size_t length = 0;
BmpBlockHeader *hdr;
ImageInfo *img;
ScreenLayout *scr;
int loc_num;
int screen_num;
int i;
int offset;
int free_data;
char image_name[80];
char full_path_name[PATH_MAX];
int yfd, bfd;
FILE *yfp = stdout;
FILE *bfp = stdout;
ptr = (char *)read_entire_file(infile, &length);
ptr = (void *)read_entire_file(infile, &length);
if (!ptr)
return 1;
@@ -81,21 +174,163 @@ int display_bmpblock(const char *infile) {
return 1;
}
if (todir) {
// Unpacking everything. Create the output directory if needed.
if (0 != require_dir(todir)) {
discard_file(ptr, length);
return 1;
}
// Open yaml output.
show_as_yaml = 1;
sprintf(full_path_name, "%s/%s", todir, "config.yaml");
yfd = open(full_path_name,
O_WRONLY | O_CREAT | O_TRUNC | (overwrite ? 0 : O_EXCL),
0666);
if (yfd < 0) {
fprintf(stderr, "Unable to open %s: %s\n", full_path_name,
strerror(errno));
discard_file(ptr, length);
return 1;
}
yfp = fdopen(yfd, "wb");
if (!yfp) {
fprintf(stderr, "Unable to fdopen %s: %s\n", full_path_name,
strerror(errno));
close(yfd);
discard_file(ptr, length);
return 1;
}
}
hdr = (BmpBlockHeader *)ptr;
printf("%s:\n", infile);
printf(" version %d.%d\n", hdr->major_version, hdr->minor_version);
printf(" %d screens\n", hdr->number_of_screenlayouts);
printf(" %d localizations\n", hdr->number_of_localizations);
printf(" %d discrete images\n", hdr->number_of_imageinfos);
if (!show_as_yaml) {
printf("%s:\n", infile);
printf(" version %d.%d\n", hdr->major_version, hdr->minor_version);
printf(" %d screens\n", hdr->number_of_screenlayouts);
printf(" %d localizations\n", hdr->number_of_localizations);
printf(" %d discrete images\n", hdr->number_of_imageinfos);
discard_file(ptr, length);
return 0;
}
// Write out yaml
fprintf(yfp, "bmpblock: %d.%d\n", hdr->major_version, hdr->minor_version);
fprintf(yfp, "images:\n");
offset = sizeof(BmpBlockHeader) +
(sizeof(ScreenLayout) *
hdr->number_of_localizations *
hdr->number_of_screenlayouts);
for(i=0; i<hdr->number_of_imageinfos; i++) {
img = (ImageInfo *)(ptr + offset);
sprintf(image_name, "img_%08x.bmp", offset);
fprintf(yfp, " img_%08x: %s # %dx%d %d/%d\n", offset, image_name,
img->width, img->height,
img->compressed_size, img->original_size);
if (todir) {
sprintf(full_path_name, "%s/%s", todir, image_name);
bfd = open(full_path_name,
O_WRONLY | O_CREAT | O_TRUNC | (overwrite ? 0 : O_EXCL),
0666);
if (bfd < 0) {
fprintf(stderr, "Unable to open %s: %s\n", full_path_name,
strerror(errno));
fclose(yfp);
discard_file(ptr, length);
return 1;
}
bfp = fdopen(bfd, "wb");
if (!bfp) {
fprintf(stderr, "Unable to fdopen %s: %s\n", full_path_name,
strerror(errno));
close(bfd);
fclose(yfp);
discard_file(ptr, length);
return 1;
}
switch(img->compression) {
case COMPRESS_NONE:
data_ptr = ptr + offset + sizeof(ImageInfo);
free_data = 0;
break;
case COMPRESS_EFIv1:
data_ptr = do_efi_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);
fclose(yfp);
discard_file(ptr, length);
return 1;
}
if (1 != fwrite(data_ptr, img->original_size, 1, bfp)) {
fprintf(stderr, "Unable to write %s: %s\n", full_path_name,
strerror(errno));
fclose(bfp);
fclose(yfp);
discard_file(ptr, length);
return 1;
}
fclose(bfp);
if (free_data)
free(data_ptr);
}
offset += sizeof(ImageInfo);
offset += img->compressed_size;
// 4-byte aligned
if ((offset & 3) > 0)
offset = (offset & ~3) + 4;
}
fprintf(yfp, "screens:\n");
for(loc_num = 0;
loc_num < hdr->number_of_localizations;
loc_num++) {
for(screen_num = 0;
screen_num < hdr->number_of_screenlayouts;
screen_num++) {
fprintf(yfp, " scr_%d_%d:\n", loc_num, screen_num);
i = loc_num * hdr->number_of_screenlayouts + screen_num;
offset = sizeof(BmpBlockHeader) + i * sizeof(ScreenLayout);
scr = (ScreenLayout *)(ptr + offset);
for(i=0; i<MAX_IMAGE_IN_LAYOUT; i++) {
if (scr->images[i].image_info_offset) {
fprintf(yfp, " - [%d, %d, img_%08x]\n",
scr->images[i].x, scr->images[i].y,
scr->images[i].image_info_offset);
}
}
}
}
fprintf(yfp, "localizations:\n");
for(loc_num = 0;
loc_num < hdr->number_of_localizations;
loc_num++) {
fprintf(yfp, " - [");
for(screen_num = 0;
screen_num < hdr->number_of_screenlayouts;
screen_num++) {
fprintf(yfp, " scr_%d_%d", loc_num, screen_num);
if (screen_num != hdr->number_of_screenlayouts - 1)
fprintf(yfp, ",");
}
fprintf(yfp, " ]\n");
}
if (todir)
fclose(yfp);
discard_file(ptr, length);
return 0;
}
int extract_bmpblock(const char *infile, const char *dirname, int force) {
printf("extract parts from %s into %s %s overwriting\n",
infile, dirname, force ? "with" : "without");
printf("NOT YET IMPLEMENTED\n");
return 0;
}

View File

@@ -16,6 +16,11 @@
#include <string.h>
#include <yaml.h>
extern "C" {
#include "eficompress.h"
}
/* BMP header, used to validate image requirements
* See http://en.wikipedia.org/wiki/BMP_file_format
*/
@@ -67,6 +72,13 @@ void BmpBlockUtil::initialize() {
config_.screens_map.clear();
config_.localizations.clear();
bmpblock_.clear();
set_compression_ = false;
compression_ = COMPRESS_NONE;
}
void BmpBlockUtil::force_compression(uint32_t compression) {
compression_ = compression;
set_compression_ = true;
}
void BmpBlockUtil::load_from_config(const char *filename) {
@@ -292,10 +304,31 @@ void BmpBlockUtil::load_all_image_files() {
const string &content = read_image_file(it->second.filename.c_str());
it->second.raw_content = content;
it->second.data.original_size = content.size();
/* Use no compression as default */
it->second.data.compression = COMPRESS_NONE;
it->second.compressed_content = content;
it->second.data.compressed_size = content.size();
switch(compression_) {
case COMPRESS_NONE:
it->second.data.compression = compression_;
it->second.compressed_content = content;
it->second.data.compressed_size = content.size();
break;
case COMPRESS_EFIv1:
{
// The content will always compress smaller (so sez the docs).
uint32_t tmpsize = content.size();
uint8_t *tmpbuf = (uint8_t *)malloc(tmpsize);
// The size of the compressed content is also returned.
if (EFI_SUCCESS != EfiCompress((uint8_t *)content.c_str(), tmpsize,
tmpbuf, &tmpsize)) {
error("Unable to compress!\n");
}
it->second.data.compression = compression_;
it->second.compressed_content.assign((const char *)tmpbuf, tmpsize);
it->second.data.compressed_size = tmpsize;
free(tmpbuf);
}
break;
default:
error("Unsupported compression method attempted.\n");
}
}
}
@@ -409,7 +442,7 @@ void BmpBlockUtil::pack_bmpblock() {
/* Compute the ImageInfo offsets from start of BMPBLOCK. */
uint32_t current_offset = sizeof(BmpBlockHeader) +
sizeof(ScreenLayout) * config_.images_map.size();
sizeof(ScreenLayout) * config_.screens_map.size();
for (StrImageConfigMap::iterator it = config_.images_map.begin();
it != config_.images_map.end();
++it) {
@@ -507,7 +540,9 @@ static void usagehelp_exit(const char *prog_name) {
printf(
"To display the contents of a BMPBLOCK:\n"
"\n"
" %s BMPBLOCK\n"
" %s [-y] BMPBLOCK\n"
"\n"
" -y = display as yaml\n"
"\n", prog_name);
printf(
"To unpack a BMPBLOCK file:\n"
@@ -531,15 +566,17 @@ int main(int argc, char *argv[]) {
else
prog_name = argv[0];
int force = 0, extract_mode = 0;
int overwrite = 0, extract_mode = 0;
int compression = 0;
int set_compression = 0;
const char *config_fn = 0, *bmpblock_fn = 0, *extract_dir = ".";
int show_as_yaml = 0;
int opt;
opterr = 0; // quiet
int errorcnt = 0;
char *e = 0;
while ((opt = getopt(argc, argv, ":c:xz:fd:")) != -1) {
while ((opt = getopt(argc, argv, ":c:xz:fd:y")) != -1) {
switch (opt) {
case 'c':
config_fn = optarg;
@@ -547,6 +584,9 @@ int main(int argc, char *argv[]) {
case 'x':
extract_mode = 1;
break;
case 'y':
show_as_yaml = 1;
break;
case 'z':
compression = (int)strtoul(optarg, &e, 0);
if (!*optarg || (e && *e)) {
@@ -556,12 +596,13 @@ int main(int argc, char *argv[]) {
}
if (compression >= MAX_COMPRESS) {
fprintf(stderr, "%s: compression type must be less than %d\n",
prog_name, compression);
prog_name, MAX_COMPRESS);
errorcnt++;
}
set_compression = 1;
break;
case 'f':
force = 1;
overwrite = 1;
break;
case 'd':
extract_dir= optarg;
@@ -594,27 +635,17 @@ int main(int argc, char *argv[]) {
BmpBlockUtil util;
if (config_fn) {
printf("compression is %d\n", compression);
if (set_compression)
util.force_compression(compression);
util.load_from_config(config_fn);
util.pack_bmpblock();
util.write_to_bmpblock(bmpblock_fn);
printf("The BMPBLOCK is sucessfully created in: %s.\n",
bmpblock_fn);
}
else if (extract_mode) {
return extract_bmpblock(bmpblock_fn, extract_dir, force);
printf("extract parts from %s into %s %s overwriting\n",
bmpblock_fn, extract_dir, force ? "with" : "without");
/* TODO(waihong): Implement the list mode. */
error("Extract mode hasn't been implemented yet.\n");
}
else {
return display_bmpblock(bmpblock_fn);
printf("display content of %s\n", bmpblock_fn);
/* TODO(waihong): Implement the list mode. */
error("List mode hasn't been implemented yet.\n");
return dump_bmpblock(bmpblock_fn, 1, extract_dir, overwrite);
} else {
return dump_bmpblock(bmpblock_fn, show_as_yaml, 0, 0);
}
return 0;

1609
utility/eficompress.c Normal file

File diff suppressed because it is too large Load Diff

1008
utility/efidecompress.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
#include "bmpblk_header.h"
int display_bmpblock(const char *infile);
int extract_bmpblock(const char *infile, const char *dirname, int force);
int dump_bmpblock(const char *infile, int show_as_yaml,
const char *todir, int overwrite);
#endif // VBOOT_REFERENCE_BMPBLK_UTIL_H_

View File

@@ -64,6 +64,9 @@ class BmpBlockUtil {
/* Write the bmpblock to a file */
void write_to_bmpblock(const char *filename);
/* What compression to use for the images */
void force_compression(uint32_t compression);
private:
/* Clear all internal data. */
void initialize();
@@ -105,6 +108,10 @@ class BmpBlockUtil {
/* Internal variable for storing the content of BmpBlock. */
string bmpblock_;
/* Internal variables to determine whether or not to specify compression */
bool set_compression_; // true if we force it
uint32_t compression_; // what we force it to
};
} // namespace vboot_reference

View File

@@ -0,0 +1,86 @@
/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#define EFI_STATUS int
#define VOID void
#define INT16 int16_t
#define UINT16 uint16_t
#define INT8 int8_t
#define UINT8 uint8_t
#define INT32 int32_t
#define UINT32 uint32_t
#define STATIC static
#define IN /**/
#define OUT /**/
#define EFIAPI /**/
#define EFIERR(a) (a)
#define EFI_SUCCESS 0
#define EFI_LOAD_ERROR EFIERR (1)
#define EFI_INVALID_PARAMETER EFIERR (2)
#define EFI_UNSUPPORTED EFIERR (3)
#define EFI_BAD_BUFFER_SIZE EFIERR (4)
#define EFI_BUFFER_TOO_SMALL EFIERR (5)
#define EFI_NOT_READY EFIERR (6)
#define EFI_DEVICE_ERROR EFIERR (7)
#define EFI_WRITE_PROTECTED EFIERR (8)
#define EFI_OUT_OF_RESOURCES EFIERR (9)
#define EFI_VOLUME_CORRUPTED EFIERR (10)
#define EFI_VOLUME_FULL EFIERR (11)
#define EFI_NO_MEDIA EFIERR (12)
#define EFI_MEDIA_CHANGED EFIERR (13)
#define EFI_NOT_FOUND EFIERR (14)
#define EFI_ACCESS_DENIED EFIERR (15)
#define EFI_NO_RESPONSE EFIERR (16)
#define EFI_NO_MAPPING EFIERR (17)
#define EFI_TIMEOUT EFIERR (18)
#define EFI_NOT_STARTED EFIERR (19)
#define EFI_ALREADY_STARTED EFIERR (20)
#define EFI_ABORTED EFIERR (21)
#define EFI_ICMP_ERROR EFIERR (22)
#define EFI_TFTP_ERROR EFIERR (23)
#define EFI_PROTOCOL_ERROR EFIERR (24)
#define EFI_INCOMPATIBLE_VERSION EFIERR (25)
#define EFI_SECURITY_VIOLATION EFIERR (26)
#define EFI_CRC_ERROR EFIERR (27)
#define EFI_END_OF_MEDIA EFIERR (28)
#define EFI_END_OF_FILE EFIERR (31)
#define EFI_INVALID_LANGUAGE EFIERR (32)
#define EFIWARN(a) ((a)+EFI_INVALID_LANGUAGE)
#define EFI_WARN_UNKNOWN_GLYPH EFIWARN (1)
#define EFI_WARN_DELETE_FAILURE EFIWARN (2)
#define EFI_WARN_WRITE_FAILURE EFIWARN (3)
#define EFI_WARN_BUFFER_TOO_SMALL EFIWARN (4)
#define EFI_ERROR(Status) (Status != 0 && Status < EFIWARN(1))
EFI_STATUS
EfiCompress (
IN UINT8 *SrcBuffer,
IN UINT32 SrcSize,
IN UINT8 *DstBuffer,
IN OUT UINT32 *DstSize
);
EFI_STATUS
EFIAPI
EfiGetInfo (
IN VOID *Source,
IN UINT32 SrcSize,
OUT UINT32 *DstSize,
OUT UINT32 *ScratchSize
);
EFI_STATUS
EFIAPI
EfiDecompress (
IN VOID *Source,
IN UINT32 SrcSize,
IN OUT VOID *Destination,
IN UINT32 DstSize,
IN OUT VOID *Scratch,
IN UINT32 ScratchSize
);