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>
diff --git a/utility/Makefile b/utility/Makefile
index 88c8a5b..b53ee57 100644
--- a/utility/Makefile
+++ b/utility/Makefile
@@ -39,7 +39,7 @@
 		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_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}/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
diff --git a/utility/bmpblk_font.c b/utility/bmpblk_font.c
new file mode 100644
index 0000000..493e87f
--- /dev/null
+++ b/utility/bmpblk_font.c
@@ -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);
+}
diff --git a/utility/bmpblk_util.c b/utility/bmpblk_util.c
index 84a48fb..49573af 100644
--- a/utility/bmpblk_util.c
+++ b/utility/bmpblk_util.c
@@ -285,20 +285,23 @@
     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 @@
           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);
           }
         }
       }
diff --git a/utility/bmpblk_utility.cc b/utility/bmpblk_utility.cc
index f4bde7c..2846746 100644
--- a/utility/bmpblk_utility.cc
+++ b/utility/bmpblk_utility.cc
@@ -6,6 +6,7 @@
 //
 
 #include "bmpblk_utility.h"
+#include "image_types.h"
 
 #include <assert.h>
 #include <errno.h>
@@ -22,29 +23,6 @@
 }
 
 
-/* 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 @@
     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 @@
       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 @@
       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 @@
         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 @@
         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 @@
       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 @@
     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 @@
          ++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. */
diff --git a/utility/image_types.c b/utility/image_types.c
new file mode 100644
index 0000000..2cc27b3
--- /dev/null
+++ b/utility/image_types.c
@@ -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;
+}
+
+
diff --git a/utility/include/bmpblk_utility.h b/utility/include/bmpblk_utility.h
index 5b70ce2..fec2b02 100644
--- a/utility/include/bmpblk_utility.h
+++ b/utility/include/bmpblk_utility.h
@@ -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 @@
 
   /* 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 @@
 
   /* 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_;
diff --git a/utility/include/image_types.h b/utility/include/image_types.h
new file mode 100644
index 0000000..f85a9a9
--- /dev/null
+++ b/utility/include/image_types.h
@@ -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_ */
+