bmpblock v1.2 - render HWID inside vboot_reference

The vboot_api.h doesn't require the BIOS display the ASCII HWID in
a graphical form (ARM U-Boot doesn't know how), so we have to do it
ourselves. This change makes that possible.

Summary of changes:
* bmpblk_font.h defines a structure to map ASCII chars to BMPs
* bmpblk_font utility generates that font structure
* bmpblock format is bumped to version 1.2
  - YAML file specifies font to use for $HWID
  - make_default_yaml updated to emit the new format
  - README updated to describe the difference

BUG=chromium-os:18631
TEST=manual

I've tested this on ARM, like so:

Inside the chroot, build a U-Boot that uses it:

  emerge-tegra2_kaen vboot_reference vboot_reference-firmware
  emerge-tegra2_kaen tegra-bct tegra2-public-firmware-fdts \
                     chromeos-u-boot chromeos-bootimage

Outside chroot, but in src/platform/vboot_reference:

  make
  <copy ./build/utility/bmpblk_font and ./build/utility/bmpblk_utility to
    somewhere in your $PATH>
  make clean

  cd scripts/newbitmaps/fonts
  bmpblk_font --outfile ../images/hwid_fonts.bin outdir/*

  cd scripts/newbitmaps/images
  make arm
  cd out_arm
  <edit DEFAULT.yaml>
  bmpblk_utility -z 2 -c DEFAULT.yaml arm_bmpblock.bin

  <use gbb_utility to replace the bitmaps in the U-Boot image, boot it>

The HWID string is displayed.

Change-Id: I782004a0f30c57fa1f3bb246e8c59a02c5e9f561
Reviewed-on: http://gerrit.chromium.org/gerrit/6544
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Tested-by: Bill Richardson <wfrichar@chromium.org>
This commit is contained in:
Bill Richardson
2011-08-22 16:03:59 -07:00
parent efea801390
commit 0a9977e161
17 changed files with 798 additions and 136 deletions

View File

@@ -54,7 +54,7 @@ __pragma(pack(push, 1)) /* Support packing for MSVC. */
#define BMPBLOCK_SIGNATURE_SIZE (4)
#define BMPBLOCK_MAJOR_VERSION (0x0001)
#define BMPBLOCK_MINOR_VERSION (0x0001)
#define BMPBLOCK_MINOR_VERSION (0x0002)
#define MAX_IMAGE_IN_LAYOUT (8)
@@ -118,6 +118,7 @@ typedef enum ImageTag {
typedef enum ImageFormat {
FORMAT_INVALID = 0,
FORMAT_BMP,
FORMAT_FONT,
} ImageFormat;
/* Constants for ImageInfo.compression */

View File

@@ -0,0 +1,65 @@
/* Copyright (c) 2011 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.
*
* This describes the internal format used to pack a set of character glpyhs so
* we can render strings by drawing one character at a time.
*
* The format is this:
*
* +-------------------------+
* | FontArrayHeader |
* +-------------------------+
* | FontArrayEntryHeader[0] |
* +-------------------------+
* | raw image data[0] |
* +-------------------------+
* | FontArrayEntryHeader[1] |
* +-------------------------+
* | raw image data[1] |
* +-------------------------+
* | FontArrayEntryHeader[2] |
* +-------------------------+
* | raw image data[2] |
* +-------------------------+
* ...
* +-------------------------+
* | FontArrayEntryHeader[n] |
* +-------------------------+
* | raw image data[n] |
* +-------------------------+
*
* The FontArrayHeader describes how many characters will be encoded.
* Each character encoding consists of a FontArrayEntryHeader followed
* immediately by the raw image data for that character.
*/
#ifndef VBOOT_REFERENCE_BMPBLK_FONT_H_
#define VBOOT_REFERENCE_BMPBLK_FONT_H_
#include "bmpblk_header.h"
__pragma(pack(push, 1)) /* Support packing for MSVC. */
#define FONT_SIGNATURE "FONT"
#define FONT_SIGNATURE_SIZE 4
typedef struct FontArrayHeader {
uint8_t signature[FONT_SIGNATURE_SIZE];
uint32_t num_entries; /* Number of chars encoded here. */
} __attribute__((packed)) FontArrayHeader;
typedef struct FontArrayEntryHeader {
uint32_t ascii; /* What to show. Could even be UTF? */
ImageInfo info; /* Describes the bitmap. */
/* The image to use follows immediately, NOT compressed. It's uncompressed
* because each glyph is only a few hundred bytes, but they have much in
* common (colormaps, for example). When we add the whole font blob to the
* bmpblk, it will be compressed as a single item there.
*/
} __attribute__((packed)) FontArrayEntryHeader;
__pragma(pack(pop)) /* Support packing for MSVC. */
#endif /* VBOOT_REFERENCE_BMPBLK_FONT_H_ */

View File

@@ -5,6 +5,7 @@
* Display functions used in kernel selection.
*/
#include "bmpblk_font.h"
#include "gbb_header.h"
#include "utility.h"
#include "vboot_api.h"
@@ -48,21 +49,131 @@ static VbError_t VbGetLocalizationCount(VbCommonParams* cparams,
/* Return a fixed string representing the HWID */
static char *VbHWID(VbCommonParams* cparams) {
GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data;
if (0 == gbb->hwid_size ||
gbb->hwid_offset > cparams->gbb_size ||
gbb->hwid_offset + gbb->hwid_size > cparams->gbb_size) {
VBDEBUG(("VbHWID(): invalid hwid offset/size\n"));
return "{INVALID}";
}
return (char*)((uint8_t*)gbb + gbb->hwid_offset);
}
/* TODO: We could cache the font info to speed things up, by making the
* in-memory font structure distinct from the in-flash version. We'll do that
* Real Soon Now. Until then, we just repeat the same linear search every time.
*/
typedef FontArrayHeader VbFont_t;
static VbFont_t *VbInternalizeFontData(FontArrayHeader *fonthdr) {
/* Just return the raw data pointer for now. */
return (VbFont_t *)fonthdr;
}
static void VbDoneWithFontForNow(VbFont_t *ptr) {
/* Nothing. */
}
static ImageInfo *VbFindFontGlyph(VbFont_t *font, uint32_t ascii,
void **bufferptr, uint32_t *buffersize) {
uint8_t *ptr, *firstptr;
uint32_t max;
uint32_t i;
FontArrayEntryHeader *entry;
ptr = (uint8_t *)font;
max = ((FontArrayHeader *)ptr)->num_entries;
ptr += sizeof(FontArrayHeader);
firstptr = ptr;
/* Simple linear search. */
for(i=0; i<max; i++)
{
entry = (FontArrayEntryHeader *)ptr;
if (entry->ascii == ascii) {
/* Note: We're assuming the glpyh is uncompressed. That's true
* because the bmpblk_font tool doesn't compress anything. The
* bmpblk_utility does, but it compresses the entire font blob at once,
* and we've already uncompressed that before we got here.
*/
*bufferptr = ptr + sizeof(FontArrayEntryHeader);
*buffersize = entry->info.original_size;
return &(entry->info);
}
ptr += sizeof(FontArrayEntryHeader)+entry->info.compressed_size;
}
/* NOTE: We must return something valid. We'll just use the first glyph in the
* font structure (so it should be something distinct).
*/
entry = (FontArrayEntryHeader *)firstptr;
*bufferptr = firstptr + sizeof(FontArrayEntryHeader);
*buffersize = entry->info.original_size;
return &(entry->info);
}
/* Try to display the specified text at a particular position. */
static void VbRenderTextAtPos(char *text, int right_to_left,
uint32_t x, uint32_t y, VbFont_t *font) {
int i;
ImageInfo *image_info = 0;
void *buffer;
uint32_t buffersize;
uint32_t cur_x = x, cur_y = y;
if (!text || !font) {
VBDEBUG((" VbRenderTextAtPos: invalid args\n"));
return;
}
for (i=0; text[i]; i++) {
if (text[i] == '\n') {
if (!image_info)
image_info = VbFindFontGlyph(font, text[i], &buffer, &buffersize);
cur_x = x;
cur_y += image_info->height;
continue;
}
image_info = VbFindFontGlyph(font, text[i], &buffer, &buffersize);
if (right_to_left) {
cur_x -= image_info->width;
}
if (VBERROR_SUCCESS != VbExDisplayImage(cur_x, cur_y, buffer, buffersize)) {
VBDEBUG((" VbRenderTextAtPos: can't display ascii 0x%x\n", text[i]));
}
if (!right_to_left) {
cur_x += image_info->width;
}
}
}
/* Display a screen from the GBB. */
VbError_t VbDisplayScreenFromGBB(VbCommonParams* cparams, uint32_t screen,
VbNvContext *vncptr) {
GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data;
uint8_t* bmpfv = NULL;
uint8_t* fullimage = NULL;
void* fullimage = NULL;
BmpBlockHeader* hdr;
ScreenLayout* layout;
ImageInfo* image_info;
uint32_t screen_index;
uint32_t localization = 0;
VbError_t retval = VBERROR_UNKNOWN; /* Assume error until proven ok */
VbError_t retval = VBERROR_UNKNOWN; /* Assume error until proven ok */
uint32_t inoutsize;
uint32_t offset;
uint32_t i;
VbFont_t *font;
char *text_to_show;
int rtol = 0;
/* Make sure the bitmap data is inside the GBB and is non-zero in size */
if (0 == gbb->bmpfv_size ||
@@ -93,26 +204,26 @@ VbError_t VbDisplayScreenFromGBB(VbCommonParams* cparams, uint32_t screen,
/* TODO: ensure screen IDs match indices? Having this translation
* here is awful. */
switch (screen) {
case VB_SCREEN_DEVELOPER_WARNING:
screen_index = 0;
break;
case VB_SCREEN_RECOVERY_REMOVE:
screen_index = 1;
break;
case VB_SCREEN_RECOVERY_NO_GOOD:
screen_index = 2;
break;
case VB_SCREEN_RECOVERY_INSERT:
screen_index = 3;
break;
case VB_SCREEN_BLANK:
case VB_SCREEN_DEVELOPER_EGG:
default:
/* Screens which aren't in the GBB */
VBDEBUG(("VbDisplayScreenFromGBB(): screen %d not in the GBB\n",
(int)screen));
retval = VBERROR_INVALID_SCREEN_INDEX;
goto VbDisplayScreenFromGBB_exit;
case VB_SCREEN_DEVELOPER_WARNING:
screen_index = 0;
break;
case VB_SCREEN_RECOVERY_REMOVE:
screen_index = 1;
break;
case VB_SCREEN_RECOVERY_NO_GOOD:
screen_index = 2;
break;
case VB_SCREEN_RECOVERY_INSERT:
screen_index = 3;
break;
case VB_SCREEN_BLANK:
case VB_SCREEN_DEVELOPER_EGG:
default:
/* Screens which aren't in the GBB */
VBDEBUG(("VbDisplayScreenFromGBB(): screen %d not in the GBB\n",
(int)screen));
retval = VBERROR_INVALID_SCREEN_INDEX;
goto VbDisplayScreenFromGBB_exit;
}
if (screen_index >= hdr->number_of_screenlayouts) {
VBDEBUG(("VbDisplayScreenFromGBB(): screen %d index %d not in the GBB\n",
@@ -131,10 +242,8 @@ VbError_t VbDisplayScreenFromGBB(VbCommonParams* cparams, uint32_t screen,
/* Calculate offset of screen layout = start of screen stuff +
* correct locale + correct screen. */
offset = sizeof(BmpBlockHeader) +
localization * hdr->number_of_screenlayouts * sizeof(ScreenLayout) +
screen_index * sizeof(ScreenLayout);
VBDEBUG(("VbDisplayScreenFromGBB(): scr_%d_%d at offset 0x%x\n",
localization, screen_index, offset));
localization * hdr->number_of_screenlayouts * sizeof(ScreenLayout) +
screen_index * sizeof(ScreenLayout);
layout = (ScreenLayout*)(bmpfv + offset);
/* Display all bitmaps for the image */
@@ -142,15 +251,10 @@ VbError_t VbDisplayScreenFromGBB(VbCommonParams* cparams, uint32_t screen,
if (layout->images[i].image_info_offset) {
offset = layout->images[i].image_info_offset;
image_info = (ImageInfo*)(bmpfv + offset);
VBDEBUG(("VbDisplayScreenFromGBB: image %d: %dx%d+%d+%d %d/%d"
"tag %d at 0x%x\n",
i, image_info->width, image_info->height,
layout->images[i].x, layout->images[i].y,
image_info->compressed_size, image_info->original_size,
image_info->tag, offset));
if (COMPRESS_NONE != image_info->compression) {
inoutsize = image_info->original_size;
fullimage = (uint8_t*)VbExMalloc(inoutsize);
fullimage = bmpfv + offset + sizeof(ImageInfo);
inoutsize = image_info->original_size;
if (inoutsize && image_info->compression != COMPRESS_NONE) {
fullimage = VbExMalloc(inoutsize);
retval = VbExDecompress(bmpfv + offset + sizeof(ImageInfo),
image_info->compressed_size,
image_info->compression,
@@ -159,16 +263,45 @@ VbError_t VbDisplayScreenFromGBB(VbCommonParams* cparams, uint32_t screen,
VbExFree(fullimage);
goto VbDisplayScreenFromGBB_exit;
}
}
switch(image_info->format) {
case FORMAT_BMP:
retval = VbExDisplayImage(layout->images[i].x, layout->images[i].y,
fullimage, inoutsize);
VbExFree(fullimage);
} else {
retval = VbExDisplayImage(layout->images[i].x, layout->images[i].y,
bmpfv + offset + sizeof(ImageInfo),
image_info->original_size);
break;
case FORMAT_FONT:
/* The uncompressed blob is our font structure. Cache it as needed. */
font = VbInternalizeFontData(fullimage);
/* TODO: handle text in general here */
if (TAG_HWID == image_info->tag || TAG_HWID_RTOL == image_info->tag) {
text_to_show = VbHWID(cparams);
rtol = (TAG_HWID_RTOL == image_info->tag);
} else {
text_to_show = "";
rtol = 0;
}
VbRenderTextAtPos(text_to_show, rtol,
layout->images[i].x, layout->images[i].y, font);
VbDoneWithFontForNow(font);
break;
default:
VBDEBUG(("VbDisplayScreenFromGBB(): unsupported ImageFormat %d\n",
image_info->format));
retval = VBERROR_INVALID_GBB;
}
if (COMPRESS_NONE != image_info->compression)
VbExFree(fullimage);
if (VBERROR_SUCCESS != retval)
goto VbDisplayScreenFromGBB_exit;
}
}
@@ -189,8 +322,6 @@ VbError_t VbDisplayScreen(VbCommonParams* cparams, uint32_t screen, int force,
VbNvContext *vncptr) {
VbError_t retval;
VBDEBUG(("VbDisplayScreen(%d, %d)\n", (int)screen, force));
/* Initialize display if necessary */
if (!disp_width) {
retval = VbExDisplayInit(&disp_width, &disp_height);

View File

@@ -49,6 +49,31 @@ last-displayed locale is stored in nvram, so it's sticky across reboots. The
factory process sets the default locale to the appropriate region.
Version 1.2. Used by any BIOS that uses "vboot_api.h"
The "vboot wrapper" is a refactoring of the vboot_reference library to
isolate our verified boot stuff from the underlying BIOS. Among other
things, it places the burden of displaying the ASCII HWID value on the
vboot_reference library, which means the bmpblock must contain a "font" to
translate the ASCII characters in the HWID into graphical images. The yaml
file must now specify a font file for the $HWID (and $HWID.rtol, if used)
image name. For example:
bmpblock: 1.2
images:
$HWID: font.bin # THIS IS NOW REQUIRED WHEN USING $HWID
screens:
scr_0_0:
- [ 0, 0, $HWID]
localizations:
- [ scr_0_0, scr_0_0, scr_0_0, scr_0_0 ]
locale_index:
en
The old v1.1 bmpblock will be accepted by the vboot wrapper, but a $HWID
screen without a corresponding font will be silently ignored.
Instructions:

View File

@@ -0,0 +1,87 @@
#!/usr/bin/python -tt
# Copyright (c) 2011 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.
import optparse
import os
import subprocess
import sys
import tempfile
chars = '* 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ{}-_'
def main():
"""Convert a set of text chars into individual BMPs.
This uses ImageMagick, so don't run it inside the build chroot.
Not all characters in the world are supported.
"""
parser = optparse.OptionParser()
parser.description = ' '.join(main.__doc__.split())
parser.add_option("--foreground", default='#9ccaec',
dest="fg", action="store", metavar="COLOR",
help="foreground color (%default)")
parser.add_option("--background", default='#607c91',
dest="bg", action="store", metavar="COLOR",
help="background color (%default)")
parser.add_option("--font", default='Helvetica',
dest="font", action="store",
help="font to use (%default)")
parser.add_option("--size", default='22', metavar="POINTSIZE",
dest="size", action="store",
help="font size (%default)")
parser.add_option('--dir', default='./outdir',
dest="outdir", action="store",
help="output directory (%default)")
(options, args) = parser.parse_args()
if not os.path.isdir(options.outdir):
os.mkdir(options.outdir)
# ARM U-Boot is very picky about its BMPs. They have to have exactly 256
# colors in their colormap. Imagemagick generally tries to reduce the
# colormap when it can, so we have to play some games to force it not to.
# We'll create a gradient file with 256 colors, and then make sure that all
# our rendered characters use the same colormap. This makes the resulting
# images larger, but it also means they'll work on x86 too. Sigh.
(handle, gradient_file) = tempfile.mkstemp(".png")
os.close(handle)
cmd = ('convert', '-size', '256x1',
'gradient:%s-%s' % (options.fg, options.bg),
gradient_file)
print ' '.join(cmd)
subprocess.call(cmd)
count=0
for ascii in chars:
outfile = os.path.join(options.outdir,
"idx%03d_%x.bmp" % (count,ord(ascii)))
print outfile
cmd = ('convert',
'-font', options.font,
'-background', options.bg,
'-fill', options.fg,
'-bordercolor', options.bg,
'-border', '0x3',
'-gravity', 'Center',
'-pointsize', options.size,
'label:%s' % ascii,
'-remap', gradient_file,
'-compress', 'none',
'-alpha', 'off',
outfile)
print ' '.join(cmd)
count += 1
subprocess.call(cmd)
os.unlink(gradient_file)
# Start it all off
if __name__ == '__main__':
main()

View File

@@ -1,11 +1,11 @@
bmpblock: 1.1
bmpblock: 1.2
compression: 1
images:
# The HWID must change for every BOM
# $HWID: hwid_unknown.bmp
# We must specify a font blob to use to render the HWID
$HWID: hwid_fonts.bin
# This URL never changes
url: Url.bmp

View File

@@ -14,6 +14,7 @@ TARGETS=x86 arm
BASE_IMAGES=Devmode.bmp Insert.bmp Remove.bmp Yuck.bmp
OTHER_IMAGES=Url.bmp hwid_unknown.bmp
FONTS=hwid_fonts.bin
default: outside_chroot
@echo "Specify a target to build for:"
@@ -36,6 +37,8 @@ ${TARGETS}:: outside_chroot
# those, so let's just assume 16:9 for future platforms to make things simpler.
_x86_max="800x600!"
_x86_scale="59%x78%"
_arm_max="800x600!"
_arm_scale="59%x78%"
x86::
# create output directories
@@ -43,6 +46,8 @@ x86::
for i in localized_images/*; do \
mkdir -p "out_$@/$$i"; \
done
# copy stuff we need
cp "${FONTS}" "out_$@"
# scale the background pictures exactly
for i in ${BASE_IMAGES}; do \
convert $$i -scale "${_x86_max}" "out_$@/$$i"; \
@@ -58,7 +63,32 @@ x86::
arm::
echo "Not sure what to do here. Please fix me."
# create output directories
mkdir -p "out_$@"
for i in localized_images/*; do \
mkdir -p "out_$@/$$i"; \
done
# copy stuff we need
cp "${FONTS}" "out_$@"
convert ${BASE_IMAGES} -append \
-colors 256 -unique-colors "out_$@/base_cmap.png"
convert localized_images/*/*.bmp -append \
-colors 256 -unique-colors "out_$@/loc_cmap.png"
# scale the background pictures exactly
for i in ${BASE_IMAGES}; do \
convert $$i -scale "${_arm_max}" \
-remap "out_$@/base_cmap.png" "out_$@/$$i"; \
done
# scale the localized string images using percentages
for i in ${OTHER_IMAGES} localized_images/*/*.bmp; do \
convert $$i -scale "${_arm_scale}" \
-remap "out_$@/loc_cmap.png" "out_$@/$$i"; \
done
# produce the new yaml
cd "out_$@" && ../make_default_yaml
# Note: hand-edit the new DEFAULT.yaml to select the shipping locales,
# then use bmpblk_utility to create the binary.
clean:

Binary file not shown.

View File

@@ -117,15 +117,15 @@ for hwid_bmp in hwid_unknown.bmp; do
echo "$yaml_file"
# List the images. The major difference is the HWID.
cat >"$yaml_file" <<EOF1
bmpblock: 1.1
cat >"$yaml_file" <<'EOF1'
bmpblock: 1.2
compression: 1
images:
# The HWID must change for every BOM
# hwid: $hwid_bmp
# We must specify a font blob to use to render the HWID
$HWID: hwid_fonts.bin
# This URL never changes
url: Url.bmp

View File

@@ -57,10 +57,13 @@ class BmpBlock(object):
# image values should all be filenames (ie, strings)
for val in images.values():
assert val and isinstance(val, types.StringTypes)
if not "$HWID" in images:
images["$HWID"] = os.path.join(self.libdir,'current_hwid.bmp')
if not "$HWID.rtol" in images:
images["$HWID.rtol"] = os.path.join(self.libdir, 'current_hwid.bmp')
# don't worry about fonts. eventually we'll have graphical mocks on host.
if "$HWID" in images:
print "WARNING: ignoring $HWID font blob"
if "$HWID.rtol" in images:
print "WARNING: ignoring $HWID.rtol font blob"
images["$HWID"] = os.path.join(self.libdir,'current_hwid.bmp')
images["$HWID.rtol"] = os.path.join(self.libdir, 'current_hwid.bmp')
screens = thing["screens"]
assert isinstance(screens, dict)

View File

@@ -39,7 +39,7 @@ TARGET_NAMES = crossystem \
vbutil_what_keys
ifeq ($(MINIMAL),)
TARGET_NAMES += bmpblk_utility eficompress efidecompress
TARGET_NAMES += bmpblk_font bmpblk_utility eficompress efidecompress
endif
TARGET_BINS = $(addprefix ${BUILD_ROOT}/,$(TARGET_NAMES))
@@ -65,6 +65,12 @@ ${BUILD_ROOT}/bmpblk_utility.o: bmpblk_utility.cc
${BUILD_ROOT}/bmpblk_util.o: bmpblk_util.c
$(CC) $(CFLAGS) -c $< -o $@
${BUILD_ROOT}/bmpblk_font.o: bmpblk_font.c
$(CC) $(CFLAGS) -c $< -o $@
${BUILD_ROOT}/image_types.o: image_types.c
$(CC) $(CFLAGS) -c $< -o $@
${BUILD_ROOT}/eficompress.o: eficompress.c
$(CC) $(CFLAGS) -c $< -o $@
@@ -79,10 +85,15 @@ ${BUILD_ROOT}/efidecompress: efidecompress.c
${BUILD_ROOT}/bmpblk_utility: ${BUILD_ROOT}/bmpblk_utility.o \
${BUILD_ROOT}/bmpblk_util.o \
${BUILD_ROOT}/image_types.o \
${BUILD_ROOT}/eficompress.o \
${BUILD_ROOT}/efidecompress.o
$(CXX) -llzma -lyaml $(CFLAGS) $^ -o $@
${BUILD_ROOT}/bmpblk_font: ${BUILD_ROOT}/bmpblk_font.o \
${BUILD_ROOT}/image_types.o
$(CC) $(CFLAGS) $^ -o $@
# TODO: rewrite load_firmware_test to support new wrapper API
#${BUILD_ROOT}/load_firmware_test: load_firmware_test.c $(LIBS)
# $(CC) $(CFLAGS) $< -o $@ $(LIBS) -lcrypto

227
utility/bmpblk_font.c Normal file
View File

@@ -0,0 +1,227 @@
// Copyright (c) 2011 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.
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include "bmpblk_font.h"
#include "image_types.h"
static char *progname;
static void error(const char *fmt, ...)
{
va_list args;
va_start( args, fmt );
fprintf(stderr, "%s: ", progname);
vfprintf( stderr, fmt, args );
va_end( args );
}
#define fatal(args...) do { error(args); exit(1); } while(0)
/* Command line options */
enum {
OPT_OUTFILE = 1000,
};
#define DEFAULT_OUTFILE "font.bin"
static struct option long_opts[] = {
{"outfile", 1, 0, OPT_OUTFILE },
{NULL, 0, 0, 0}
};
/* Print help and return error */
static void HelpAndDie(void) {
fprintf(stderr,
"\n"
"%s - Create a vboot fontfile from a set of BMP files.\n"
"\n"
"Usage: %s [OPTIONS] BMPFILE [BMPFILE...]\n"
"\n"
"Each BMP file must match *_HEX.bmp, where HEX is the hexadecimal\n"
"representation of the character that the file displays. The images\n"
"will be encoded in the given order. Typically the first image is\n"
"reused to represent any missing characters.\n"
"\n"
"OPTIONS are:\n"
" --outfile <filename> Output file (default is %s)\n"
"\n", progname, progname, DEFAULT_OUTFILE);
exit(1);
}
//////////////////////////////////////////////////////////////////////////////
// Returns pointer to buffer containing entire file, sets length.
static void *read_entire_file(const char *filename, size_t *length) {
int fd;
struct stat sbuf;
void *ptr;
*length = 0; // just in case
if (0 != stat(filename, &sbuf)) {
error("Unable to stat %s: %s\n", filename, strerror(errno));
return 0;
}
if (!sbuf.st_size) {
error("File %s is empty\n", filename);
return 0;
}
fd = open(filename, O_RDONLY);
if (fd < 0) {
error("Unable to open %s: %s\n", filename, strerror(errno));
return 0;
}
ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (MAP_FAILED == ptr) {
error("Unable to mmap %s: %s\n", filename, strerror(errno));
close(fd);
return 0;
}
*length = sbuf.st_size;
close(fd);
return ptr;
}
// Reclaims buffer from read_entire_file().
static void discard_file(void *ptr, size_t length) {
munmap(ptr, length);
}
//////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[]) {
char* outfile = DEFAULT_OUTFILE;
int numimages = 0;
int parse_error = 0;
int i;
FILE *ofp;
FontArrayHeader header;
FontArrayEntryHeader entry;
progname = strrchr(argv[0], '/');
if (progname)
progname++;
else
progname = argv[0];
while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) {
switch (i) {
case OPT_OUTFILE:
outfile = optarg;
break;
default:
/* Unhandled option */
printf("Unknown option\n");
parse_error = 1;
break;
}
}
numimages = argc - optind;
if (parse_error || numimages < 1)
HelpAndDie();
printf("outfile is %s\n", outfile);
printf("numimages is %d\n", numimages);
ofp = fopen(outfile, "wb");
if (!ofp)
fatal("Unable to open %s: %s\n", outfile, strerror(errno));
memcpy(&header.signature, FONT_SIGNATURE, FONT_SIGNATURE_SIZE);
header.num_entries = numimages;
if (1 != fwrite(&header, sizeof(header), 1, ofp)) {
error("Can't write header to %s: %s\n", outfile, strerror(errno));
goto bad1;
}
for(i=0; i<numimages; i++) {
char *imgfile = argv[optind+i];
char *s;
uint32_t ascii;
void *imgdata = 0;
size_t imgsize, filesize, diff;
s = strrchr(imgfile, '_');
if (!s || 1 != sscanf(s, "_%x.bmp", &ascii)) { // This is not foolproof.
error("Unable to parse the character from filename %s\n", imgfile);
goto bad1;
}
imgdata = read_entire_file(imgfile, &imgsize);
if (!imgdata)
goto bad1;
if (FORMAT_BMP != identify_image_type(imgdata, imgsize, &entry.info)) {
error("%s does not contain a valid BMP image\n", imgfile);
goto bad1;
}
// Pad the image to align it on a 4-byte boundary.
filesize = imgsize;
if (imgsize % 4)
filesize = ((imgsize + 4) / 4) * 4;
diff = filesize - imgsize;
entry.ascii = ascii;
entry.info.tag = TAG_NONE;
entry.info.compression = COMPRESS_NONE; // we'll compress it all later
entry.info.original_size = filesize;
entry.info.compressed_size = filesize;
printf("%s => 0x%x %dx%d\n", imgfile, entry.ascii,
entry.info.width, entry.info.height);
if (1 != fwrite(&entry, sizeof(entry), 1, ofp)) {
error("Can't write entry to %s: %s\n", outfile, strerror(errno));
goto bad1;
}
if (1 != fwrite(imgdata, imgsize, 1, ofp)) {
error("Can't write image to %s: %s\n", outfile, strerror(errno));
goto bad1;
}
if (diff && 1 != fwrite("\0\0\0\0\0\0\0\0", diff, 1, ofp)) {
error("Can't write padding to %s: %s\n", outfile, strerror(errno));
goto bad1;
}
discard_file(imgdata, imgsize);
}
fclose(ofp);
return 0;
bad1:
fclose(ofp);
error("Aborting\n");
(void) unlink(outfile);
exit(1);
}

View File

@@ -285,20 +285,23 @@ int dump_bmpblock(const char *infile, int show_as_yaml,
if (img->compressed_size) {
sprintf(image_name, "img_%08x.bmp", offset);
if (img->tag == TAG_HWID) {
fprintf(yfp, " %s: %s # %dx%d %d/%d tag=%d\n",
fprintf(yfp, " %s: %s # %dx%d %d/%d tag=%d fmt=%d\n",
RENDER_HWID, image_name,
img->width, img->height,
img->compressed_size, img->original_size, img->tag);
img->compressed_size, img->original_size,
img->tag, img->format);
} else if (img->tag == TAG_HWID_RTOL) {
fprintf(yfp, " %s: %s # %dx%d %d/%d tag=%d\n",
fprintf(yfp, " %s: %s # %dx%d %d/%d tag=%d fmt=%d\n",
RENDER_HWID_RTOL, image_name,
img->width, img->height,
img->compressed_size, img->original_size, img->tag);
img->compressed_size, img->original_size,
img->tag, img->format);
} else {
fprintf(yfp, " img_%08x: %s # %dx%d %d/%d tag=%d\n",
fprintf(yfp, " img_%08x: %s # %dx%d %d/%d tag=%d fmt=%d\n",
offset, image_name,
img->width, img->height,
img->compressed_size, img->original_size, img->tag);
img->compressed_size, img->original_size,
img->tag, img->format);
}
if (todir) {
sprintf(full_path_name, "%s/%s", todir, image_name);
@@ -388,17 +391,23 @@ int dump_bmpblock(const char *infile, int show_as_yaml,
ImageInfo *iptr =
(ImageInfo *)(ptr + scr->images[i].image_info_offset);
if (iptr->tag == TAG_HWID) {
fprintf(yfp, " - [%d, %d, %s]\n",
fprintf(yfp, " - [%d, %d, %s] # tag=%d fmt=%d c=%d %d/%d\n",
scr->images[i].x, scr->images[i].y,
RENDER_HWID);
RENDER_HWID, iptr->tag, iptr->format, iptr->compression,
iptr->compressed_size, iptr->original_size);
} else if (iptr->tag == TAG_HWID_RTOL) {
fprintf(yfp, " - [%d, %d, %s]\n",
fprintf(yfp, " - [%d, %d, %s] # tag=%d fmt=%d c=%d %d/%d\n",
scr->images[i].x, scr->images[i].y,
RENDER_HWID_RTOL);
RENDER_HWID_RTOL, iptr->tag,
iptr->format, iptr->compression,
iptr->compressed_size, iptr->original_size);
} else {
fprintf(yfp, " - [%d, %d, img_%08x]\n",
fprintf(yfp, " - [%d, %d, img_%08x]"
" # tag=%d fmt=%d c=%d %d/%d\n",
scr->images[i].x, scr->images[i].y,
scr->images[i].image_info_offset);
scr->images[i].image_info_offset,
iptr->tag, iptr->format, iptr->compression,
iptr->compressed_size, iptr->original_size);
}
}
}

View File

@@ -6,6 +6,7 @@
//
#include "bmpblk_utility.h"
#include "image_types.h"
#include <assert.h>
#include <errno.h>
@@ -22,29 +23,6 @@ extern "C" {
}
/* BMP header, used to validate image requirements
* See http://en.wikipedia.org/wiki/BMP_file_format
*/
typedef struct {
uint8_t CharB; // must be 'B'
uint8_t CharM; // must be 'M'
uint32_t Size;
uint16_t Reserved[2];
uint32_t ImageOffset;
uint32_t HeaderSize;
uint32_t PixelWidth;
uint32_t PixelHeight;
uint16_t Planes; // Must be 1 for x86
uint16_t BitPerPixel; // 1, 4, 8, or 24 for x86
uint32_t CompressionType; // 0 (none) for x86, 1 (RLE) for arm
uint32_t ImageSize;
uint32_t XPixelsPerMeter;
uint32_t YPixelsPerMeter;
uint32_t NumberOfColors;
uint32_t ImportantColors;
} __attribute__((packed)) BMP_IMAGE_HEADER;
static void error(const char *format, ...) {
va_list ap;
va_start(ap, format);
@@ -71,6 +49,10 @@ namespace vboot_reference {
set_compression_ = false;
compression_ = COMPRESS_NONE;
debug_ = debug;
render_hwid_ = true;
support_font_ = true;
got_font_ = false;
got_rtol_font_ = false;
}
BmpBlockUtil::~BmpBlockUtil() {
@@ -125,11 +107,12 @@ namespace vboot_reference {
for (StrImageConfigMap::iterator it = config_.images_map.begin();
it != config_.images_map.end();
++it) {
printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d\n",
printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d fmt=%d\n",
it->first.c_str(),
it->second.filename.c_str(),
it->second.offset,
it->second.data.tag);
it->second.data.tag,
it->second.data.format);
}
printf("%ld screens_map\n", config_.screens_map.size());
for (StrScreenConfigMap::iterator it = config_.screens_map.begin();
@@ -206,11 +189,19 @@ namespace vboot_reference {
error("Syntax error in parsing bmpblock.\n");
}
string gotversion = (char*)event.data.scalar.value;
if (gotversion == "1.1") {
if (gotversion == "1.2") {
render_hwid_ = true;
support_font_ = true;
} else if (gotversion == "1.1") {
minor_version_ = 1;
render_hwid_ = true;
support_font_ = false;
fprintf(stderr, "WARNING: using old format: %s\n", gotversion.c_str());
} else if (gotversion == "1.0") {
minor_version_ = 0;
render_hwid_ = false;
support_font_ = false;
fprintf(stderr, "WARNING: using old format: %s\n", gotversion.c_str());
} else {
error("Unsupported version specified in config file (%s)\n",
gotversion.c_str());
@@ -254,6 +245,12 @@ namespace vboot_reference {
config_.image_names.push_back(image_name);
config_.images_map[image_name] = ImageConfig();
config_.images_map[image_name].filename = image_filename;
if (image_name == RENDER_HWID) {
got_font_ = true;
}
if (image_name == RENDER_HWID_RTOL) {
got_rtol_font_ = true;
}
break;
case YAML_MAPPING_END_EVENT:
yaml_event_delete(&event);
@@ -286,17 +283,22 @@ namespace vboot_reference {
case 2:
screen.image_names[index1] = (char*)event.data.scalar.value;
// Detect the special case where we're rendering the HWID string
// instead of displaying a bitmap. The image name shouldn't
// exist in the list of images, but we will still need an
// instead of displaying a bitmap. The image name may not
// exist in the list of images (v1.1), but we will still need an
// ImageInfo struct to remember where to draw the text.
// Note that if the image name DOES exist, we still will won't
// display it (yet). Future versions may use that image to hold the
// font glpyhs, which is why we pass it around now.
// Note that v1.2 requires that the image name DOES exist, because
// the corresponding file is used to hold the font glpyhs.
if (render_hwid_) {
if (screen.image_names[index1] == RENDER_HWID) {
config_.images_map[RENDER_HWID].data.tag = TAG_HWID;
if (support_font_ && !got_font_)
error("Font required in 'image:' section for %s\n",
RENDER_HWID);
} else if (screen.image_names[index1] == RENDER_HWID_RTOL) {
config_.images_map[RENDER_HWID_RTOL].data.tag = TAG_HWID_RTOL;
if (support_font_ && !got_rtol_font_)
error("Font required in 'image:' section for %s\n",
RENDER_HWID_RTOL);
}
}
break;
@@ -406,13 +408,10 @@ namespace vboot_reference {
const string &content = read_image_file(it->second.filename.c_str());
it->second.raw_content = content;
it->second.data.original_size = content.size();
it->second.data.format = get_image_format(content);
switch (it->second.data.format) {
case FORMAT_BMP:
it->second.data.width = get_bmp_image_width(it->second.raw_content);
it->second.data.height = get_bmp_image_height(it->second.raw_content);
break;
default:
it->second.data.format =
identify_image_type(content.c_str(),
(uint32_t)content.size(), &it->second.data);
if (FORMAT_INVALID == it->second.data.format) {
error("Unsupported image format in %s\n", it->second.filename.c_str());
}
switch(compression_) {
@@ -503,31 +502,6 @@ namespace vboot_reference {
return content;
}
ImageFormat BmpBlockUtil::get_image_format(const string content) {
if (content.size() < sizeof(BMP_IMAGE_HEADER))
return FORMAT_INVALID;
const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
if (hdr->CharB != 'B' || hdr->CharM != 'M' ||
hdr->Planes != 1 ||
(hdr->CompressionType != 0 && hdr->CompressionType != 1) ||
(hdr->BitPerPixel != 1 && hdr->BitPerPixel != 4 &&
hdr->BitPerPixel != 8 && hdr->BitPerPixel != 24))
return FORMAT_INVALID;
return FORMAT_BMP;
}
uint32_t BmpBlockUtil::get_bmp_image_width(const string content) {
const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
return hdr->PixelWidth;
}
uint32_t BmpBlockUtil::get_bmp_image_height(const string content) {
const BMP_IMAGE_HEADER *hdr = (const BMP_IMAGE_HEADER *)content.c_str();
return hdr->PixelHeight;
}
void BmpBlockUtil::fill_bmpblock_header() {
memset(&config_.header, '\0', sizeof(config_.header));
memcpy(&config_.header.signature, BMPBLOCK_SIGNATURE,
@@ -557,11 +531,12 @@ namespace vboot_reference {
++it) {
it->second.offset = current_offset;
if (debug_)
printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d\n",
printf(" \"%s\": filename=\"%s\" offset=0x%x tag=%d fmt=%d\n",
it->first.c_str(),
it->second.filename.c_str(),
it->second.offset,
it->second.data.tag);
it->second.data.tag,
it->second.data.format);
current_offset += sizeof(ImageInfo) +
it->second.data.compressed_size;
/* Make it 4-byte aligned. */

71
utility/image_types.c Normal file
View File

@@ -0,0 +1,71 @@
// 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.
#include <stdint.h>
#include <string.h>
#include "bmpblk_header.h"
#include "bmpblk_font.h"
#include "image_types.h"
/* BMP header, used to validate image requirements
* See http://en.wikipedia.org/wiki/BMP_file_format
*/
typedef struct {
uint8_t CharB; // must be 'B'
uint8_t CharM; // must be 'M'
uint32_t Size;
uint16_t Reserved[2];
uint32_t ImageOffset;
uint32_t HeaderSize;
uint32_t PixelWidth;
uint32_t PixelHeight;
uint16_t Planes; // Must be 1 for x86
uint16_t BitPerPixel; // 1, 4, 8, or 24 for x86
uint32_t CompressionType; // 0 (none) for x86, 1 (RLE) for arm
uint32_t ImageSize;
uint32_t XPixelsPerMeter;
uint32_t YPixelsPerMeter;
uint32_t NumberOfColors;
uint32_t ImportantColors;
} __attribute__((packed)) BMP_IMAGE_HEADER;
ImageFormat identify_image_type(const void *buf, uint32_t bufsize,
ImageInfo *info) {
if (info)
info->format = FORMAT_INVALID;
if (bufsize < sizeof(BMP_IMAGE_HEADER) &&
bufsize < sizeof(FontArrayHeader)) {
return FORMAT_INVALID;
}
const BMP_IMAGE_HEADER *bhdr = buf;
if (bhdr->CharB == 'B' && bhdr->CharM == 'M' &&
bhdr->Planes == 1 &&
(bhdr->CompressionType == 0 || bhdr->CompressionType == 1) &&
(bhdr->BitPerPixel == 1 || bhdr->BitPerPixel == 4 ||
bhdr->BitPerPixel == 8 || bhdr->BitPerPixel == 24)) {
if (info) {
info->format = FORMAT_BMP;
info->width = bhdr->PixelWidth;
info->height = bhdr->PixelHeight;
}
return FORMAT_BMP;
}
const FontArrayHeader *fhdr = buf;
if (0 == memcmp(&fhdr->signature, FONT_SIGNATURE, FONT_SIGNATURE_SIZE) &&
fhdr->num_entries > 0) {
if (info)
info->format = FORMAT_FONT;
return FORMAT_FONT;
}
return FORMAT_BMP;
}

View File

@@ -7,6 +7,8 @@
#define VBOOT_REFERENCE_BMPBLK_UTILITY_H_
#include "bmpblk_header.h"
#include "bmpblk_font.h"
#include "image_types.h"
#include <yaml.h>
@@ -93,9 +95,6 @@ class BmpBlockUtil {
/* Useful functions */
const string read_image_file(const char *filename);
ImageFormat get_image_format(const string content);
uint32_t get_bmp_image_width(const string content);
uint32_t get_bmp_image_height(const string content);
/* Verbosity flags */
bool debug_;
@@ -106,6 +105,9 @@ class BmpBlockUtil {
/* Flags for version-specific features */
bool render_hwid_;
bool support_font_;
bool got_font_;
bool got_rtol_font_;
/* Internal variable for storing the config of BmpBlock. */
BmpBlockConfig config_;

View File

@@ -0,0 +1,25 @@
/* Copyright (c) 2011 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.
*/
#ifndef VBOOT_REFERENCE_IMAGE_TYPES_H_
#define VBOOT_REFERENCE_IMAGE_TYPES_H_
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
#include <stdint.h>
#include "bmpblk_header.h"
/* Identify the data. Fill in known values if info is not NULL */
ImageFormat identify_image_type(const void *buf, uint32_t bufsize,
ImageInfo *info);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* VBOOT_REFERENCE_IMAGE_TYPES_H_ */