vboot: cgpt: Support writing GPT structs to NOR flash

This CL allows the GPT headers and partition entry arrays to be stored
in a NOR flash device. Instead of treating both the NOR and NAND devices
as one (in a sandwich way), this CL writes and reads the GPT structs
independently of the actual device that houses the partitions.
Therefore, the first usable LBA of the partitions will be at 0, and the
last usable LBA is at the end of the NAND.

  +------------------------+
  | NOR houses GPT structs |
  +------------------------+
        |
  0     |  Index into
  v     v
  +------------------------+
  | NAND houses partitions |
  +------------------------+

Note that the "my_lba", "alternate_lba", "entries_lba" in the GPT headers
are no longer meaningful.

Consumers of cgptlib will have to set "stored_on_device" to either
GPT_STORED_ON_DEVICE or GPT_STORED_OFF_DEVICE, and "gpt_drive_sectors"
to the number of 512-byte sectors available to store GPT structs.

The NOR read and write operations are done by "flashrom".

BUG=chromium:425677
BRANCH=none
TEST=unittest
TEST=build with DEBUG, cgpt create/add/show on a stumpy-moblab

Change-Id: I083b3c94da3b0bb3da1a7b10c6969774080a2afd
Reviewed-on: https://chromium-review.googlesource.com/226800
Reviewed-by: Nam Nguyen <namnguyen@chromium.org>
Commit-Queue: Nam Nguyen <namnguyen@chromium.org>
Tested-by: Nam Nguyen <namnguyen@chromium.org>
diff --git a/Makefile b/Makefile
index 146c042..87ef807 100644
--- a/Makefile
+++ b/Makefile
@@ -360,6 +360,7 @@
 	cgpt/cgpt_repair.c \
 	cgpt/cgpt_prioritize.c \
 	cgpt/cgpt_common.c \
+	cgpt/drive.c \
 	cgpt/flash_ts.c \
 	cgpt/flash_ts_drv.c \
 	firmware/lib/cgptlib/mtdlib.c \
@@ -389,6 +390,7 @@
 	cgpt/cgpt_common.c \
 	cgpt/cgpt_create.c \
 	cgpt/cgpt_prioritize.c \
+	cgpt/drive.c \
 	cgpt/flash_ts.c \
 	cgpt/flash_ts_drv.c \
 	firmware/lib/cgptlib/cgptlib_internal.c \
@@ -404,6 +406,7 @@
 	futility/dump_kernel_config_lib.c \
 	host/arch/${ARCH}/lib/crossystem_arch.c \
 	host/lib/crossystem.c \
+	host/lib/fmap.c \
 	host/lib/host_misc.c
 
 HOSTLIB_OBJS = ${HOSTLIB_SRCS:%.c=${BUILD}/%.o}
@@ -420,6 +423,7 @@
 	cgpt/cgpt_common.c \
 	cgpt/cgpt_create.c \
 	cgpt/cgpt_prioritize.c \
+	cgpt/drive.c \
 	cgpt/flash_ts.c \
 	cgpt/flash_ts_drv.c \
 	firmware/lib/cgptlib/cgptlib_internal.c \
@@ -427,7 +431,8 @@
 	firmware/lib/cgptlib/mtdlib.c \
 	firmware/lib/utility_string.c \
 	firmware/stub/utility_stub.c \
-	futility/dump_kernel_config_lib.c
+	futility/dump_kernel_config_lib.c \
+	host/lib/fmap.c
 
 TINYHOSTLIB_OBJS = ${TINYHOSTLIB_SRCS:%.c=${BUILD}/%.o}
 
diff --git a/cgpt/cgpt.h b/cgpt/cgpt.h
index aa47314..ea459cb 100644
--- a/cgpt/cgpt.h
+++ b/cgpt/cgpt.h
@@ -12,6 +12,7 @@
 #include <stdlib.h>
 #include "cgpt_endian.h"
 #include "cgptlib.h"
+#include "drive.h"
 #include "gpt.h"
 #include "mtdlib.h"
 
@@ -28,7 +29,6 @@
   uint32_t num_sect;
 } __attribute__((packed));
 
-
 // syslinux uses this format:
 struct pmbr {
   uint8_t                 bootcode[424];
@@ -43,12 +43,33 @@
 
 // Handle to the drive storing the GPT.
 struct drive {
-  int fd;           /* file descriptor */
   uint64_t size;    /* total size (in bytes) */
   int is_mtd;
   GptData gpt;
   MtdData mtd;
   struct pmbr pmbr;
+  /*
+   * For use with regular file or block device.
+   * GPT structures will occupy the first and last few blocks.
+   */
+  struct {
+    int fd;       /* file descriptor */
+  };
+  /*
+   * For use with flash.
+   * GPT structures will be stored in flash, while the partitions are in
+   * /dev/mtd*.
+   */
+  struct {
+    off_t current_position;  /* for used by DriveSeekFunc */
+    uint32_t flash_start;    /* offset where we can write to flash, in FMAP */
+    uint32_t flash_size;     /* size of that area, in bytes, in FMAP */
+  };  /* for use with flashrom */
+  DriveSeekFunc seek;
+  DriveReadFunc read;
+  DriveWriteFunc write;
+  DriveSyncFunc sync;
+  DriveCloseFunc close;
 };
 
 struct nand_layout {
@@ -61,7 +82,11 @@
 void EnableNandImage(int bytes_per_page, int pages_per_block,
                      int fts_block_offset, int fts_block_size);
 
-/* mode should be O_RDONLY or O_RDWR */
+// Opens a block device or file, loads raw GPT data from it.
+// mode should be O_RDONLY or O_RDWR
+//
+// Returns CGPT_FAILED if any error happens.
+// Returns CGPT_OK if success and information are stored in 'drive'.
 int DriveOpen(const char *drive_path, struct drive *drive, int mode);
 int DriveClose(struct drive *drive, int update_as_needed);
 int CheckValid(const struct drive *drive);
@@ -191,6 +216,7 @@
 
 // For usage and error messages.
 void Error(const char *format, ...);
+void Warning(const char *format, ...);
 
 // Command functions.
 int cmd_show(int argc, char *argv[]);
diff --git a/cgpt/cgpt_common.c b/cgpt/cgpt_common.c
index 3900cf3..43312fe 100644
--- a/cgpt/cgpt_common.c
+++ b/cgpt/cgpt_common.c
@@ -9,6 +9,8 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <getopt.h>
+#include <linux/major.h>
+#include <mtd/mtd-user.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <stdio.h>
@@ -75,7 +77,6 @@
                 const uint64_t sector_count) {
   int count;  /* byte count to read */
   int nread;
-  int fd = drive->fd;
 
   require(buf);
   if (!sector_count || !sector_bytes) {
@@ -93,12 +94,12 @@
   *buf = malloc(count);
   require(*buf);
 
-  if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET)) {
-    Error("Can't lseek: %s\n", strerror(errno));
+  if (-1 == drive->seek(drive, sector * sector_bytes, SEEK_SET)) {
+    Error("Can't seek: %s\n", strerror(errno));
     goto error_free;
   }
 
-  nread = read(fd, *buf, count);
+  nread = drive->read(drive, *buf, count);
   if (nread < count) {
     Error("Can't read enough: %d, not %d\n", nread, count);
     goto error_free;
@@ -114,10 +115,10 @@
 
 
 int ReadPMBR(struct drive *drive) {
-  if (-1 == lseek(drive->fd, 0, SEEK_SET))
+  if (-1 == drive->seek(drive, 0, SEEK_SET))
     return CGPT_FAILED;
 
-  int nread = read(drive->fd, &drive->pmbr, sizeof(struct pmbr));
+  int nread = drive->read(drive, &drive->pmbr, sizeof(struct pmbr));
   if (nread != sizeof(struct pmbr))
     return CGPT_FAILED;
 
@@ -125,10 +126,10 @@
 }
 
 int WritePMBR(struct drive *drive) {
-  if (-1 == lseek(drive->fd, 0, SEEK_SET))
+  if (-1 == drive->seek(drive, 0, SEEK_SET))
     return CGPT_FAILED;
 
-  int nwrote = write(drive->fd, &drive->pmbr, sizeof(struct pmbr));
+  int nwrote = drive->write(drive, &drive->pmbr, sizeof(struct pmbr));
   if (nwrote != sizeof(struct pmbr))
     return CGPT_FAILED;
 
@@ -141,15 +142,14 @@
                 const uint64_t sector_count) {
   int count;  /* byte count to write */
   int nwrote;
-  int fd = drive->fd;
 
   require(buf);
   count = sector_bytes * sector_count;
 
-  if (-1 == lseek(fd, sector * sector_bytes, SEEK_SET))
+  if (-1 == drive->seek(drive, sector * sector_bytes, SEEK_SET))
     return CGPT_FAILED;
 
-  nwrote = write(fd, buf, count);
+  nwrote = drive->write(drive, buf, count);
   if (nwrote < count)
     return CGPT_FAILED;
 
@@ -320,6 +320,11 @@
   }
   drive->gpt.drive_sectors = drive->size / drive->gpt.sector_bytes;
 
+  /* TODO(namnguyen): Remove this and totally trust gpt_drive_sectors. */
+  if (drive->gpt.stored_on_device == GPT_STORED_ON_DEVICE) {
+    drive->gpt.gpt_drive_sectors = drive->gpt.drive_sectors;
+  } /* Else, we trust gpt.gpt_drive_sectors. */
+
   // Read the data.
   if (CGPT_OK != Load(drive, &drive->gpt.primary_header,
                       GPT_PMBR_SECTORS,
@@ -328,13 +333,14 @@
     return -1;
   }
   if (CGPT_OK != Load(drive, &drive->gpt.secondary_header,
-                      drive->gpt.drive_sectors - GPT_PMBR_SECTORS,
+                      drive->gpt.gpt_drive_sectors - GPT_PMBR_SECTORS,
                       drive->gpt.sector_bytes, GPT_HEADER_SECTORS)) {
     Error("Cannot read secondary GPT header\n");
     return -1;
   }
   GptHeader* primary_header = (GptHeader*)drive->gpt.primary_header;
-  if (CheckHeader(primary_header, 0, drive->gpt.drive_sectors) == 0) {
+  if (CheckHeader(primary_header, 0, drive->gpt.drive_sectors,
+                  drive->gpt.stored_on_device) == 0) {
     if (CGPT_OK != Load(drive, &drive->gpt.primary_entries,
                         primary_header->entries_lba,
                         drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
@@ -345,7 +351,8 @@
     Warning("Primary GPT header is invalid\n");
   }
   GptHeader* secondary_header = (GptHeader*)drive->gpt.secondary_header;
-  if (CheckHeader(secondary_header, 1, drive->gpt.drive_sectors) == 0) {
+  if (CheckHeader(secondary_header, 1, drive->gpt.drive_sectors,
+                  drive->gpt.stored_on_device) == 0) {
     if (CGPT_OK != Load(drive, &drive->gpt.secondary_entries,
                         secondary_header->entries_lba,
                         drive->gpt.sector_bytes, GPT_ENTRIES_SECTORS)) {
@@ -371,7 +378,7 @@
 
   if (drive->gpt.modified & GPT_MODIFIED_HEADER2) {
     if(CGPT_OK != Save(drive, drive->gpt.secondary_header,
-                       drive->gpt.drive_sectors - GPT_PMBR_SECTORS,
+                       drive->gpt.gpt_drive_sectors - GPT_PMBR_SECTORS,
                        drive->gpt.sector_bytes, GPT_HEADER_SECTORS)) {
       errors++;
       Error("Cannot write secondary header: %s\n", strerror(errno));
@@ -427,6 +434,7 @@
 
   // Clear struct for proper error handling.
   memset(drive, 0, sizeof(struct drive));
+  drive->gpt.stored_on_device = GPT_STORED_ON_DEVICE;
 
   if (TryInitMtd(drive_path)) {
     is_mtd = 1;
@@ -438,11 +446,37 @@
       return CGPT_FAILED;
     }
 
+    drive->seek = FileSeek;
+    drive->read = FileRead;
+    drive->write = FileWrite;
+    drive->sync = FileSync;
+    drive->close = FileClose;
     if (fstat(drive->fd, &stat) == -1) {
       Error("Can't fstat %s: %s\n", drive_path, strerror(errno));
       goto error_close;
     }
-    if ((stat.st_mode & S_IFMT) != S_IFREG) {
+    if (major(stat.st_rdev) == MTD_CHAR_MAJOR) {
+      mtd_info_t mtd_info;
+      if (ioctl(drive->fd, MEMGETINFO, &mtd_info) != 0) {
+        Error("Can't get the size of the MTD device\n");
+        goto error_close;
+      }
+      drive->size = mtd_info.size;
+
+      if (FlashInit(drive) != 0) {
+        Error("Can't obtain NOR flash info with flashrom\n");
+        goto error_close;
+      }
+
+      sector_bytes = 512;
+      drive->gpt.stored_on_device = GPT_STORED_OFF_DEVICE;
+      drive->gpt.gpt_drive_sectors = drive->flash_size / sector_bytes;
+      drive->seek = FlashSeek;
+      drive->read = FlashRead;
+      drive->write = FlashWrite;
+      drive->sync = FlashSync;
+      drive->close = FlashClose;
+    } else if ((stat.st_mode & S_IFMT) != S_IFREG) {
       if (ioctl(drive->fd, BLKGETSIZE64, &drive->size) < 0) {
         Error("Can't read drive size from %s: %s\n", drive_path,
               strerror(errno));
@@ -501,9 +535,9 @@
   // Sync early! Only sync file descriptor here, and leave the whole system sync
   // outside cgpt because whole system sync would trigger tons of disk accesses
   // and timeout tests.
-  fsync(drive->fd);
+  drive->sync(drive);
 
-  close(drive->fd);
+  drive->close(drive);
 
   return errors ? CGPT_FAILED : CGPT_OK;
 }
@@ -1173,7 +1207,7 @@
     }
   } else if (valid_headers == MASK_PRIMARY) {
     memcpy(secondary_header, primary_header, sizeof(GptHeader));
-    secondary_header->my_lba = gpt->drive_sectors - 1;  /* the last sector */
+    secondary_header->my_lba = gpt->gpt_drive_sectors - 1;  /* the last sector */
     secondary_header->alternate_lba = primary_header->my_lba;
     secondary_header->entries_lba = secondary_header->my_lba -
         GPT_ENTRIES_SECTORS;
diff --git a/cgpt/cgpt_create.c b/cgpt/cgpt_create.c
index 7d3c059..6a21a8b 100644
--- a/cgpt/cgpt_create.c
+++ b/cgpt/cgpt_create.c
@@ -44,17 +44,40 @@
     h->revision = GPT_HEADER_REVISION;
     h->size = sizeof(GptHeader);
     h->my_lba = GPT_PMBR_SECTORS;  /* The second sector on drive. */
-    h->alternate_lba = drive->gpt.drive_sectors - GPT_HEADER_SECTORS;
-    h->entries_lba = h->my_lba + GPT_HEADER_SECTORS + params->padding;
-    h->first_usable_lba = h->entries_lba + GPT_ENTRIES_SECTORS;
-    h->last_usable_lba = (drive->gpt.drive_sectors - GPT_HEADER_SECTORS -
-                          GPT_ENTRIES_SECTORS - 1);
+    h->alternate_lba = drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS;
+    h->entries_lba = h->my_lba + GPT_HEADER_SECTORS;
+    if (drive->gpt.stored_on_device == GPT_STORED_ON_DEVICE) {
+      h->entries_lba += params->padding;
+      h->first_usable_lba = h->entries_lba + GPT_ENTRIES_SECTORS;
+      h->last_usable_lba = (drive->gpt.drive_sectors - GPT_HEADER_SECTORS -
+                            GPT_ENTRIES_SECTORS - 1);
+    } else {
+      h->first_usable_lba = 0;
+      h->last_usable_lba = (drive->gpt.drive_sectors - 1);
+    }
     if (CGPT_OK != GenerateGuid(&h->disk_uuid)) {
       Error("Unable to generate new GUID.\n");
       return -1;
     }
-    h->number_of_entries = 128;
     h->size_of_entry = sizeof(GptEntry);
+    h->number_of_entries = TOTAL_ENTRIES_SIZE / h->size_of_entry;
+    if (drive->gpt.stored_on_device != GPT_STORED_ON_DEVICE) {
+      // We might have smaller space for the GPT table. Scale accordingly.
+      size_t half_size = drive->flash_size / 2;
+      size_t header_block_size = GPT_HEADER_SECTORS * drive->gpt.sector_bytes;
+      if (half_size < header_block_size) {
+        Error("Not enough space for a GPT header.\n");
+        return -1;
+      }
+      half_size -= header_block_size;
+      if (half_size < MIN_NUMBER_OF_ENTRIES * h->size_of_entry) {
+        Error("Not enough space for minimum number of entries.\n");
+        return -1;
+      }
+      if (128 > half_size / h->size_of_entry) {
+        h->number_of_entries = half_size / h->size_of_entry;
+      }
+    }
 
     // Copy to secondary
     RepairHeader(&drive->gpt, MASK_PRIMARY);
diff --git a/cgpt/cgpt_show.c b/cgpt/cgpt_show.c
index 4788bfd..5b7974f 100644
--- a/cgpt/cgpt_show.c
+++ b/cgpt/cgpt_show.c
@@ -445,7 +445,7 @@
     }
 
     if (drive->gpt.valid_headers & MASK_SECONDARY)
-      printf(GPT_FMT, (int)(drive->gpt.drive_sectors - GPT_HEADER_SECTORS),
+      printf(GPT_FMT, (int)(drive->gpt.gpt_drive_sectors - GPT_HEADER_SECTORS),
              (int)GPT_HEADER_SECTORS, "", "Sec GPT header");
     else
       printf(GPT_FMT, (int)GPT_PMBR_SECTORS,
diff --git a/cgpt/drive.c b/cgpt/drive.c
new file mode 100644
index 0000000..db6601d
--- /dev/null
+++ b/cgpt/drive.c
@@ -0,0 +1,305 @@
+/* Copyright 2014 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 <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cgpt.h"
+#include "fmap.h"
+
+// TODO(namnguyen): Remove RW_UNUSED
+#ifdef DEBUG
+static const char FMAP_GPT_SECTION[] = "RW_UNUSED";
+#else
+static const char FMAP_GPT_SECTION[] = "RW_GPT";
+#endif
+
+off_t FileSeek(struct drive* drive, off_t offset, int whence) {
+  return lseek(drive->fd, offset, whence);
+}
+
+ssize_t FileRead(struct drive* drive, void* buf, size_t count) {
+  return read(drive->fd, buf, count);
+}
+
+ssize_t FileWrite(struct drive* drive, const void* buf, size_t count) {
+  return write(drive->fd, buf, count);
+}
+
+int FileSync(struct drive* drive) {
+  return fsync(drive->fd);
+}
+
+int FileClose(struct drive* drive) {
+  return close(drive->fd);
+}
+
+// Always terminate the buffer after snprintf.
+static int tsnprintf(char *buf, size_t size, const char* fmt, ...) {
+  if (size == 0) {
+    // No space for the null char.
+    errno = ENOSPC;
+    return -1;
+  }
+  va_list ap;
+  va_start(ap, fmt);
+  int ret = vsnprintf(buf, size, fmt, ap);
+  va_end(ap);
+  if (ret >= 0) {
+    buf[size - 1] = '\x00';
+  }
+  return ret;
+}
+
+int FlashInit(struct drive* drive) {
+  int return_code = 1;
+  char tempdir[] = "/tmp/cgptXXXXXX";
+  if (mkdtemp(tempdir) == NULL) {
+    Error("Cannot create temp directory for flashrom work.\n");
+    return return_code;
+  }
+
+  char cmd[256];
+  char fmap_name[28];
+  tsnprintf(fmap_name, sizeof(fmap_name), "%s/fmap", tempdir);
+  tsnprintf(cmd, sizeof(cmd), "/usr/sbin/flashrom -p host -i FMAP:%s -r "
+            "> /dev/null 2>&1", fmap_name);
+  return_code++;
+  if (system(cmd) != 0) {
+    Error("Cannot dump FMAP section from flash.\n");
+    goto cleanup;
+  };
+
+  return_code++;
+  int fmap_fd = open(fmap_name, O_RDONLY);
+  if (fmap_fd < 0) {
+    Error("Cannot open %s.\n", fmap_name);
+    goto cleanup;
+  }
+  // Allocate 4096 bytes. ChromeOS FMAP is usually 2048 bytes.
+  return_code++;
+  const size_t fmap_alloc_size = 4096;
+  uint8_t* fmap = malloc(fmap_alloc_size);
+  if (!fmap) {
+    Error("Cannot read fmap.\n");
+    goto cleanup2;
+  }
+  return_code++;
+  int fmap_size = read(fmap_fd, fmap, fmap_alloc_size);
+  if (fmap_size < 0) {
+    Error("Cannot read from %s.\n", fmap_name);
+    goto cleanup3;
+  }
+
+  return_code++;
+  FmapAreaHeader* gpt_area;
+  if (fmap_find_by_name(fmap, fmap_size, NULL,
+                        FMAP_GPT_SECTION, &gpt_area) == NULL) {
+    Error("Cannot find GPT section in the FMAP.\n");
+    goto cleanup3;
+  }
+
+  drive->flash_start = gpt_area->area_offset;
+  drive->flash_size = gpt_area->area_size;
+  drive->current_position = 0;
+
+  return_code = 0;
+
+cleanup3:
+  free(fmap);
+cleanup2:
+  close(fmap_fd);
+cleanup:
+  tsnprintf(cmd, sizeof(cmd), "/bin/rm -rf %s", tempdir);
+  if (system(cmd)) {
+    Warning("Cannot remove temp directory", tempdir);
+  }
+  return return_code;
+}
+
+off_t FlashSeek(struct drive* drive, off_t offset, int whence) {
+  off_t new_position;
+  switch (whence) {
+    case SEEK_SET:
+      new_position = offset;
+      break;
+    case SEEK_CUR:
+      new_position = drive->current_position + offset;
+      break;
+    case SEEK_END:
+      new_position = drive->size + offset;
+      break;
+    default:
+      errno = EINVAL;
+      return -1;
+  }
+  if (new_position < 0 || new_position > drive->size) {
+    errno = EINVAL;
+    return -1;
+  }
+  drive->current_position = new_position;
+  return new_position;
+}
+
+// Translate |position| to an address in flash.
+// We only use a small area in flash to store the GPT structures. This area is
+// identified in FMAP. So the idea is to map |position| from 0 to flash_size to
+// the physical position in flash linearly.
+// This function returns 0 for success.
+static int TranslateToFlash(struct drive* drive, off_t position, size_t count,
+                            off_t* translated) {
+  if (position < 0 || position + count > drive->flash_size) {
+    return -1;
+  }
+  *translated = position + drive->flash_start;
+  return 0;
+}
+
+static int CreateLayout(char* file_name, off_t position, size_t count) {
+  int fd = mkstemp(file_name);
+  if (fd < 0) {
+    Error("Cannot create layout file.\n");
+    return -1;
+  }
+  char buf[128];
+  tsnprintf(buf, sizeof(buf), "%08X:%08X landmark\n", (unsigned int) position,
+            (unsigned int) (position + count - 1));
+  int layout_len = strlen(buf);
+  int nr_written = write(fd, buf, layout_len);
+  close(fd);
+  if (nr_written != layout_len) {
+    Error("Cannot write out layout for flashrom.\n");
+    return -1;
+  }
+
+  return 0;
+}
+
+ssize_t FlashRead(struct drive* drive, void* buf, size_t count) {
+  off_t offset;
+  if (TranslateToFlash(drive, drive->current_position, count, &offset) != 0) {
+    Error("Cannot translate disk address %08X to SPI address.\n",
+          drive->current_position);
+    errno = EINVAL;
+    return -1;
+  }
+
+  char tempdir[] = "/tmp/cgptXXXXXX";
+  if (mkdtemp(tempdir) == NULL) {
+    Error("Cannot create temp directory for flashrom work.\n");
+    errno = EIO;
+    return -1;
+  }
+
+  int return_value = -1;
+  char layout_file[40];
+  tsnprintf(layout_file, sizeof(layout_file), "%s/layoutXXXXXX", tempdir);
+  if (CreateLayout(layout_file, offset, count) != 0) {
+    Error("Cannot create layout file for flashrom.\n");
+    goto cleanup;
+  }
+
+  char content_file[40];
+  tsnprintf(content_file, sizeof(content_file), "%s/contentXXXXXX", tempdir);
+  int fd = mkstemp(content_file);
+  if (fd < 0) {
+    goto cleanup;
+  }
+
+  char cmd[256];
+  tsnprintf(cmd, sizeof(cmd), "/usr/sbin/flashrom -p host -l %s -i landmark:%s "
+            "-r > /dev/null 2>&1", layout_file, content_file);
+  if (system(cmd) != 0) {
+    Error("Cannot read from SPI flash.\n");
+    goto cleanup2;
+  }
+
+  return_value = read(fd, buf, count);
+  if (return_value != count) {
+    Error("Cannot read from retrieved content file.\n");
+    return_value = -1;
+  } else {
+    drive->current_position += return_value;
+  }
+
+cleanup2:
+  close(fd);
+cleanup:
+  tsnprintf(cmd, sizeof(cmd), "/bin/rm -rf %s", tempdir);
+  if (system(cmd)) {
+    Warning("Cannot remove temp directory", tempdir);
+  }
+  errno = EIO;
+  return return_value;
+}
+
+ssize_t FlashWrite(struct drive* drive, const void* buf, size_t count) {
+  off_t offset;
+  if (TranslateToFlash(drive, drive->current_position, count, &offset) != 0) {
+    Error("Cannot translate disk address %08X to SPI address.\n",
+          drive->current_position);
+    errno = EINVAL;
+    return -1;
+  }
+
+  char tempdir[] = "/tmp/cgptXXXXXX";
+  if (mkdtemp(tempdir) == NULL) {
+    Error("Cannot create temp directory for flashrom work.\n");
+    errno = EIO;
+    return -1;
+  }
+
+  int return_value = -1;
+  char layout_file[40];
+  tsnprintf(layout_file, sizeof(layout_file), "%s/layoutXXXXXX", tempdir);
+  if (CreateLayout(layout_file, offset, count) != 0) {
+    Error("Cannot create layout file for flashrom.\n");
+    goto cleanup;
+  }
+
+  char content_file[40];
+  tsnprintf(content_file, sizeof(content_file), "%s/contentXXXXXX", tempdir);
+  int fd = mkstemp(content_file);
+  if (fd < 0) {
+    goto cleanup;
+  }
+
+  return_value = write(fd, buf, count);
+  close(fd);
+  if (return_value != count) {
+    Error("Cannot prepare content file for flashrom.\n");
+    return_value = -1;
+    goto cleanup;
+  }
+
+  char cmd[256];
+  // TODO(namnguyen): Add --fast-verify after 428475 is fixed
+  tsnprintf(cmd, sizeof(cmd), "/usr/sbin/flashrom -p host -l %s -i landmark:%s "
+            "-w > /dev/null 2>&1", layout_file, content_file);
+  if (system(cmd) != 0) {
+    Error("Cannot write to SPI flash.\n");
+    return_value = -1;
+  } else {
+    drive->current_position += return_value;
+  }
+
+cleanup:
+  tsnprintf(cmd, sizeof(cmd), "/bin/rm -rf %s", tempdir);
+  if (system(cmd)) {
+    Warning("Cannot remove temp directory", tempdir);
+  }
+  errno = EIO;
+  return return_value;
+}
+
+int FlashSync(struct drive* drive) {
+  return 0;
+}
+
+int FlashClose(struct drive* drive) {
+  return 0;
+}
diff --git a/cgpt/drive.h b/cgpt/drive.h
new file mode 100644
index 0000000..d03bfe7
--- /dev/null
+++ b/cgpt/drive.h
@@ -0,0 +1,29 @@
+/* Copyright 2014 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_UTILITY_CGPT_DRIVE_H_
+#define VBOOT_REFERENCE_UTILITY_CGPT_DRIVE_H_
+
+struct drive;
+typedef off_t (*DriveSeekFunc)(struct drive*, off_t offset, int whence);
+typedef ssize_t (*DriveReadFunc)(struct drive*, void* buf, size_t count);
+typedef ssize_t (*DriveWriteFunc)(struct drive*, const void* buf, size_t count);
+typedef int (*DriveCloseFunc)(struct drive*);
+typedef int (*DriveSyncFunc)(struct drive*);
+
+off_t FileSeek(struct drive* drive, off_t offset, int whence);
+ssize_t FileRead(struct drive* drive, void* buf, size_t count);
+ssize_t FileWrite(struct drive* drive, const void* buf, size_t count);
+int FileSync(struct drive* drive);
+int FileClose(struct drive* drive);
+
+int FlashInit(struct drive* drive);
+off_t FlashSeek(struct drive* drive, off_t offset, int whence);
+ssize_t FlashRead(struct drive* drive, void* buf, size_t count);
+ssize_t FlashWrite(struct drive* drive, const void* buf, size_t count);
+int FlashSync(struct drive* drive);
+int FlashClose(struct drive* drive);
+
+#endif  // VBOOT_REFERENCE_UTILITY_CGPT_DRIVE_H_
diff --git a/firmware/include/gpt_misc.h b/firmware/include/gpt_misc.h
index 4d91bd8..53b3034 100644
--- a/firmware/include/gpt_misc.h
+++ b/firmware/include/gpt_misc.h
@@ -57,6 +57,24 @@
 	GPT_UPDATE_ENTRY_BAD = 2,
 };
 
+enum {
+	GPT_STORED_ON_DEVICE = 0,   /* The GPT is stored on the same device. */
+	GPT_STORED_OFF_DEVICE = 1,  /* The GPT is stored on another place. */
+};
+
+/*
+ * A note about stored_on_device and gpt_drive_sectors:
+ *
+ * This code is used by both the "cgpt" utility and depthcharge/vboot. ATM,
+ * depthcharge does not have logic to properly setup stored_on_device and
+ * gpt_drive_sectors, but it does do a memset(gpt, 0, sizeof(GptData)). And so,
+ * GPT_STORED_ON_DEVICE should be 0 to make stored_on_device compatible with
+ * present behavior. At the same time, in vboot_kernel:LoadKernel(), and
+ * cgpt_common:GptLoad(), we need to have simple shims to set gpt_drive_sectors
+ * to drive_sectors.
+ *
+ * TODO(namnguyen): Remove those shims when the firmware can set these fields.
+ */
 typedef struct {
 	/* Fill in the following fields before calling GptInit() */
 	/* GPT primary header, from sector 1 of disk (size: 512 bytes) */
@@ -69,8 +87,12 @@
 	uint8_t *secondary_entries;
 	/* Size of a LBA sector, in bytes */
 	uint32_t sector_bytes;
-	/* Size of drive in LBA sectors, in sectors */
+	/* Size of drive (that the partitions are on) in LBA sectors */
 	uint64_t drive_sectors;
+	/* Are the GPT structures stored on the same device */
+	uint8_t stored_on_device;
+	/* Size of the device that holds the GPT structures, 512-byte sectors */
+	uint64_t gpt_drive_sectors;
 
 	/* Outputs */
 	/* Which inputs have been modified?  GPT_MODIFIED_* */
@@ -98,6 +120,8 @@
  *   secondary_entries
  *   sector_bytes
  *   drive_sectors
+ *   stored_on_device
+ *   gpt_device_sectors
  *
  * On return the modified field may be set, if the GPT data has been modified
  * and should be written to disk.
diff --git a/firmware/lib/cgptlib/cgptlib_internal.c b/firmware/lib/cgptlib/cgptlib_internal.c
index 4eea03d..e9f27c9 100644
--- a/firmware/lib/cgptlib/cgptlib_internal.c
+++ b/firmware/lib/cgptlib/cgptlib_internal.c
@@ -20,6 +20,16 @@
 		return GPT_ERROR_INVALID_SECTOR_SIZE;
 
 	/*
+	 * gpt_drive_sectors should be reasonable. It cannot be unset, and it cannot
+	 * differ from drive_sectors if the GPT structs are stored on same device.
+	 */
+	if (gpt->gpt_drive_sectors == 0 ||
+		(gpt->stored_on_device == GPT_STORED_ON_DEVICE &&
+			gpt->gpt_drive_sectors != gpt->drive_sectors)) {
+		return GPT_ERROR_INVALID_SECTOR_NUMBER;
+	}
+
+	/*
 	 * Sector count of a drive should be reasonable. If the given value is
 	 * too small to contain basic GPT structure (PMBR + Headers + Entries),
 	 * the value is wrong.
@@ -43,7 +53,8 @@
 	return crc32;
 }
 
-int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors)
+int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors,
+	uint8_t stored_on_device)
 {
 	if (!h)
 		return 1;
@@ -80,7 +91,8 @@
 		return 1;
 	if ((h->number_of_entries < MIN_NUMBER_OF_ENTRIES) ||
 	    (h->number_of_entries > MAX_NUMBER_OF_ENTRIES) ||
-	    (h->number_of_entries * h->size_of_entry != TOTAL_ENTRIES_SIZE))
+	    (stored_on_device == GPT_STORED_ON_DEVICE &&
+	    h->number_of_entries * h->size_of_entry != TOTAL_ENTRIES_SIZE))
 		return 1;
 
 	/*
@@ -100,18 +112,27 @@
 			return 1;
 	}
 
+	/* FirstUsableLBA <= LastUsableLBA. */
+	if (h->first_usable_lba > h->last_usable_lba)
+		return 1;
+
+	if (stored_on_device != GPT_STORED_ON_DEVICE) {
+		if (h->last_usable_lba >= drive_sectors) {
+			return 1;
+		}
+		return 0;
+	}
+
 	/*
 	 * FirstUsableLBA must be after the end of the primary GPT table array.
 	 * LastUsableLBA must be before the start of the secondary GPT table
-	 * array.  FirstUsableLBA <= LastUsableLBA.
+	 * array.
 	 */
 	/* TODO(namnguyen): Also check for padding between header & entries. */
 	if (h->first_usable_lba < 2 + GPT_ENTRIES_SECTORS)
 		return 1;
 	if (h->last_usable_lba >= drive_sectors - 1 - GPT_ENTRIES_SECTORS)
 		return 1;
-	if (h->first_usable_lba > h->last_usable_lba)
-		return 1;
 
 	/* Success */
 	return 0;
@@ -224,11 +245,11 @@
 		return retval;
 
 	/* Check both headers; we need at least one valid header. */
-	if (0 == CheckHeader(header1, 0, gpt->drive_sectors)) {
+	if (0 == CheckHeader(header1, 0, gpt->drive_sectors, gpt->stored_on_device)) {
 		gpt->valid_headers |= MASK_PRIMARY;
 		goodhdr = header1;
 	}
-	if (0 == CheckHeader(header2, 1, gpt->drive_sectors)) {
+	if (0 == CheckHeader(header2, 1, gpt->drive_sectors, gpt->stored_on_device)) {
 		gpt->valid_headers |= MASK_SECONDARY;
 		if (!goodhdr)
 			goodhdr = header2;
diff --git a/firmware/lib/cgptlib/include/cgptlib_internal.h b/firmware/lib/cgptlib/include/cgptlib_internal.h
index e9e63ca..4a52420 100644
--- a/firmware/lib/cgptlib/include/cgptlib_internal.h
+++ b/firmware/lib/cgptlib/include/cgptlib_internal.h
@@ -90,7 +90,8 @@
  *
  * Returns 0 if header is valid, 1 if invalid.
  */
-int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors);
+int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors,
+                uint8_t stored_on_device);
 
 /**
  * Calculate and return the header CRC.
diff --git a/firmware/lib/gpt_misc.c b/firmware/lib/gpt_misc.c
index d00d6b2..499cc9e 100644
--- a/firmware/lib/gpt_misc.c
+++ b/firmware/lib/gpt_misc.c
@@ -16,7 +16,7 @@
 /**
  * Allocate and read GPT data from the drive.
  *
- * The sector_bytes and drive_sectors fields should be filled on input.  The
+ * The sector_bytes and gpt_drive_sectors fields should be filled on input.  The
  * primary and secondary header and entries are filled on output.
  *
  * Returns 0 if successful, 1 if error.
@@ -48,7 +48,8 @@
 
 	/* Only read primary GPT if the primary header is valid */
 	GptHeader* primary_header = (GptHeader*)gptdata->primary_header;
-	if (0 == CheckHeader(primary_header, 0, gptdata->drive_sectors)) {
+	if (0 == CheckHeader(primary_header, 0, gptdata->gpt_drive_sectors,
+			gptdata->stored_on_device)) {
 		primary_valid = 1;
 		if (0 != VbExDiskRead(disk_handle,
 				      primary_header->entries_lba,
@@ -60,13 +61,14 @@
 	}
 
 	/* Read secondary header from the end of the drive */
-	if (0 != VbExDiskRead(disk_handle, gptdata->drive_sectors - 1, 1,
+	if (0 != VbExDiskRead(disk_handle, gptdata->gpt_drive_sectors - 1, 1,
 			      gptdata->secondary_header))
 		return 1;
 
 	/* Only read secondary GPT if the secondary header is valid */
 	GptHeader* secondary_header = (GptHeader*)gptdata->secondary_header;
-	if (0 == CheckHeader(secondary_header, 1, gptdata->drive_sectors)) {
+	if (0 == CheckHeader(secondary_header, 1, gptdata->gpt_drive_sectors,
+			gptdata->stored_on_device)) {
 		secondary_valid = 1;
 		if (0 != VbExDiskRead(disk_handle,
 				      secondary_header->entries_lba,
@@ -138,7 +140,7 @@
 		}
 	}
 
-	entries_lba = (gptdata->drive_sectors - entries_sectors -
+	entries_lba = (gptdata->gpt_drive_sectors - entries_sectors -
 		GPT_HEADER_SECTORS);
 	if (gptdata->secondary_header) {
 		GptHeader *h = (GptHeader *)(gptdata->secondary_header);
@@ -146,7 +148,7 @@
 		if (gptdata->modified & GPT_MODIFIED_HEADER2) {
 			VBDEBUG(("Updating GPT entries 2\n"));
 			if (0 != VbExDiskWrite(disk_handle,
-					       gptdata->drive_sectors - 1, 1,
+					       gptdata->gpt_drive_sectors - 1, 1,
 					       gptdata->secondary_header))
 				goto fail;
 		}
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c
index b2cb817..62e6296 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -116,6 +116,9 @@
 	/* Read GPT data */
 	gpt.sector_bytes = (uint32_t)blba;
 	gpt.drive_sectors = params->ending_lba + 1;
+	/* TODO: Set stored_on_device and gpt_drive_sectors appropriately */
+	gpt.stored_on_device = GPT_STORED_ON_DEVICE;
+	gpt.gpt_drive_sectors = gpt.drive_sectors;
 	if (0 != AllocAndReadGptData(params->disk_handle, &gpt)) {
 		VBDEBUG(("Unable to read GPT data\n"));
 		shcall->check_result = VBSD_LKC_CHECK_GPT_READ_ERROR;
diff --git a/tests/cgptlib_test.c b/tests/cgptlib_test.c
index 363ecc8..12b366a 100644
--- a/tests/cgptlib_test.c
+++ b/tests/cgptlib_test.c
@@ -164,7 +164,7 @@
 	Guid chromeos_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS;
 
 	gpt->sector_bytes = DEFAULT_SECTOR_SIZE;
-	gpt->drive_sectors = DEFAULT_DRIVE_SECTORS;
+	gpt->drive_sectors = gpt->gpt_drive_sectors = DEFAULT_DRIVE_SECTORS;
 	gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND;
 	gpt->valid_headers = MASK_BOTH;
 	gpt->valid_entries = MASK_BOTH;
@@ -353,7 +353,7 @@
 	for (i = 0; i < ARRAY_SIZE(cases); ++i) {
 		BuildTestGptData(gpt);
 		gpt->sector_bytes = cases[i].sector_bytes;
-		gpt->drive_sectors = cases[i].drive_sectors;
+		gpt->drive_sectors = gpt->gpt_drive_sectors = cases[i].drive_sectors;
 		EXPECT(cases[i].expected_retval == CheckParameters(gpt));
 	}
 
@@ -461,15 +461,15 @@
 	GptHeader *h2 = (GptHeader *)gpt->secondary_header;
 	int i;
 
-	EXPECT(1 == CheckHeader(NULL, 0, gpt->drive_sectors));
+	EXPECT(1 == CheckHeader(NULL, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 
 	for (i = 0; i < 8; ++i) {
 		BuildTestGptData(gpt);
 		h1->signature[i] ^= 0xff;
 		h2->signature[i] ^= 0xff;
 		RefreshCrc32(gpt);
-		EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
-		EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
+		EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+		EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 	}
 
 	return TEST_OK;
@@ -503,9 +503,9 @@
 		h2->revision = cases[i].value_to_test;
 		RefreshCrc32(gpt);
 
-		EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) ==
+		EXPECT(CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE) ==
 		       cases[i].expect_rv);
-		EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) ==
+		EXPECT(CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE) ==
 		       cases[i].expect_rv);
 	}
 	return TEST_OK;
@@ -536,9 +536,9 @@
 		h2->size = cases[i].value_to_test;
 		RefreshCrc32(gpt);
 
-		EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) ==
+		EXPECT(CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE) ==
 		       cases[i].expect_rv);
-		EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) ==
+		EXPECT(CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE) ==
 		       cases[i].expect_rv);
 	}
 	return TEST_OK;
@@ -555,12 +555,12 @@
 	/* Modify a field that the header verification doesn't care about */
 	h1->entries_crc32++;
 	h2->entries_crc32++;
-	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
-	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
+	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 	/* Refresh the CRC; should pass now */
 	RefreshCrc32(gpt);
-	EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors));
-	EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors));
+	EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 
 	return TEST_OK;
 }
@@ -576,8 +576,8 @@
 	h1->reserved_zero ^= 0x12345678;  /* whatever random */
 	h2->reserved_zero ^= 0x12345678;  /* whatever random */
 	RefreshCrc32(gpt);
-	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
-	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
+	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 
 #ifdef PADDING_CHECKED
 	/* TODO: padding check is currently disabled */
@@ -585,8 +585,8 @@
 	h1->padding[12] ^= 0x34;  /* whatever random */
 	h2->padding[56] ^= 0x78;  /* whatever random */
 	RefreshCrc32(gpt);
-	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
-	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
+	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 #endif
 
 	return TEST_OK;
@@ -624,9 +624,9 @@
 			cases[i].value_to_test;
 		RefreshCrc32(gpt);
 
-		EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) ==
+		EXPECT(CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE) ==
 		       cases[i].expect_rv);
-		EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) ==
+		EXPECT(CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE) ==
 		       cases[i].expect_rv);
 	}
 
@@ -647,8 +647,11 @@
 	h1->number_of_entries--;
 	h2->number_of_entries /= 2;
 	RefreshCrc32(gpt);
-	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
-	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
+	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	/* But it's okay to have less if the GPT structs are stored elsewhere. */
+	EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_OFF_DEVICE));
+	EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_OFF_DEVICE));
 
 	return TEST_OK;
 }
@@ -663,37 +666,37 @@
 
 	/* myLBA depends on primary vs secondary flag */
 	BuildTestGptData(gpt);
-	EXPECT(1 == CheckHeader(h1, 1, gpt->drive_sectors));
-	EXPECT(1 == CheckHeader(h2, 0, gpt->drive_sectors));
+	EXPECT(1 == CheckHeader(h1, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	EXPECT(1 == CheckHeader(h2, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 
 	BuildTestGptData(gpt);
 	h1->my_lba--;
 	h2->my_lba--;
 	RefreshCrc32(gpt);
-	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
-	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
+	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 
 	BuildTestGptData(gpt);
 	h1->my_lba = 2;
 	h2->my_lba--;
 	RefreshCrc32(gpt);
-	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
-	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
+	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 
 	/* We should ignore the alternate_lba field entirely */
 	BuildTestGptData(gpt);
 	h1->alternate_lba++;
 	h2->alternate_lba++;
 	RefreshCrc32(gpt);
-	EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors));
-	EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors));
+	EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 
 	BuildTestGptData(gpt);
 	h1->alternate_lba--;
 	h2->alternate_lba--;
 	RefreshCrc32(gpt);
-	EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors));
-	EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors));
+	EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 
 	BuildTestGptData(gpt);
 	h1->entries_lba++;
@@ -703,19 +706,19 @@
 	 * We support a padding between primary GPT header and its entries. So
 	 * this still passes.
 	 */
-	EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors));
+	EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 	/*
 	 * But the secondary table should fail because it would overlap the
 	 * header, which is now lying after its entry array.
 	 */
-	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
+	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 
 	BuildTestGptData(gpt);
 	h1->entries_lba--;
 	h2->entries_lba--;
 	RefreshCrc32(gpt);
-	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors));
-	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors));
+	EXPECT(1 == CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
+	EXPECT(1 == CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE));
 
 	return TEST_OK;
 }
@@ -723,7 +726,8 @@
 /* Test if FirstUsableLBA and LastUsableLBA are checked.
  * FirstUsableLBA must be after the end of the primary GPT table array.
  * LastUsableLBA must be before the start of the secondary GPT table array.
- * FirstUsableLBA <= LastUsableLBA. */
+ * FirstUsableLBA <= LastUsableLBA.
+ * Also see CheckHeaderOffDevice() test. */
 static int FirstUsableLbaAndLastUsableLbaTest(void)
 {
 	GptData *gpt = GetEmptyGptData();
@@ -763,9 +767,9 @@
 		h2->last_usable_lba = cases[i].secondary_last_usable_lba;
 		RefreshCrc32(gpt);
 
-		EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) ==
+		EXPECT(CheckHeader(h1, 0, gpt->drive_sectors, GPT_STORED_ON_DEVICE) ==
 		       cases[i].primary_rv);
-		EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) ==
+		EXPECT(CheckHeader(h2, 1, gpt->drive_sectors, GPT_STORED_ON_DEVICE) ==
 		       cases[i].secondary_rv);
 	}
 
@@ -1966,6 +1970,64 @@
   return TEST_OK;
 }
 
+static int CheckHeaderOffDevice()
+{
+	GptData* gpt = GetEmptyGptData();
+	BuildTestGptData(gpt);
+
+	GptHeader* primary_header = (GptHeader*)gpt->primary_header;
+	primary_header->first_usable_lba = 0;
+	RefreshCrc32(gpt);
+	// GPT is stored on the same device so first usable lba should not
+	// start at 0.
+	EXPECT(1 == CheckHeader(primary_header, 0, gpt->drive_sectors,
+		GPT_STORED_ON_DEVICE));
+	// But off device, it is okay to accept this GPT header.
+	EXPECT(0 == CheckHeader(primary_header, 0, gpt->drive_sectors,
+		GPT_STORED_OFF_DEVICE));
+
+	BuildTestGptData(gpt);
+	primary_header->number_of_entries = 100;
+	RefreshCrc32(gpt);
+	// Normally, number of entries is 128. So this should fail.
+	EXPECT(1 == CheckHeader(primary_header, 0, gpt->drive_sectors,
+		GPT_STORED_ON_DEVICE));
+	// But off device, it is okay.
+	EXPECT(0 == CheckHeader(primary_header, 0, gpt->drive_sectors,
+		GPT_STORED_OFF_DEVICE));
+
+	primary_header->number_of_entries = MIN_NUMBER_OF_ENTRIES - 1;
+	RefreshCrc32(gpt);
+	// However, too few entries is not good.
+	EXPECT(1 == CheckHeader(primary_header, 0, gpt->drive_sectors,
+		GPT_STORED_OFF_DEVICE));
+
+	// Repeat for secondary header.
+	BuildTestGptData(gpt);
+	GptHeader* secondary_header = (GptHeader*)gpt->secondary_header;
+	secondary_header->first_usable_lba = 0;
+	RefreshCrc32(gpt);
+	EXPECT(1 == CheckHeader(secondary_header, 1, gpt->drive_sectors,
+		GPT_STORED_ON_DEVICE));
+	EXPECT(0 == CheckHeader(secondary_header, 1, gpt->drive_sectors,
+		GPT_STORED_OFF_DEVICE));
+
+	BuildTestGptData(gpt);
+	secondary_header->number_of_entries = 100;
+	RefreshCrc32(gpt);
+	EXPECT(1 == CheckHeader(secondary_header, 1, gpt->drive_sectors,
+		GPT_STORED_ON_DEVICE));
+	EXPECT(0 == CheckHeader(secondary_header, 1, gpt->drive_sectors,
+		GPT_STORED_OFF_DEVICE));
+
+	secondary_header->number_of_entries = MIN_NUMBER_OF_ENTRIES - 1;
+	RefreshCrc32(gpt);
+	EXPECT(1 == CheckHeader(secondary_header, 1, gpt->drive_sectors,
+		GPT_STORED_OFF_DEVICE));
+
+	return TEST_OK;
+}
+
 int main(int argc, char *argv[])
 {
 	int i;
@@ -2013,6 +2075,7 @@
 		{ TEST_CASE(GetKernelGuidTest), },
 		{ TEST_CASE(ErrorTextTest), },
 		{ TEST_CASE(MtdFtsTest), },
+		{ TEST_CASE(CheckHeaderOffDevice), },
 	};
 
 	for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) {
diff --git a/tests/run_cgpt_tests.sh b/tests/run_cgpt_tests.sh
index 847c081..95840fb 100755
--- a/tests/run_cgpt_tests.sh
+++ b/tests/run_cgpt_tests.sh
@@ -21,6 +21,12 @@
 warning "testing $CGPT in $DIR"
 cd "$DIR"
 
+# Test failure on non existing file.
+set +e
+${CGPT} show blah_404_haha
+[ $? != 0 ] || error "CGPT should fail on non existing file."
+set -e
+
 echo "Create an empty file to use as the device..."
 NUM_SECTORS=1000
 DEV=fake_dev.bin
diff --git a/tests/vboot_kernel_tests.c b/tests/vboot_kernel_tests.c
index 0c26d21..05035f2 100644
--- a/tests/vboot_kernel_tests.c
+++ b/tests/vboot_kernel_tests.c
@@ -289,7 +289,7 @@
 	GptHeader *h;
 
 	g.sector_bytes = MOCK_SECTOR_SIZE;
-	g.drive_sectors = MOCK_SECTOR_COUNT;
+	g.drive_sectors = g.gpt_drive_sectors = MOCK_SECTOR_COUNT;
 	g.valid_headers = g.valid_entries = MASK_BOTH;
 
 	ResetMocks();
@@ -315,10 +315,10 @@
 	Memset(mock_gpt_primary, '\0', sizeof(*mock_gpt_primary));
 	TEST_EQ(AllocAndReadGptData(handle, &g), 0,
 		"AllocAndRead primary invalid");
-	TEST_EQ(CheckHeader(mock_gpt_primary, 0, g.drive_sectors), 1,
-		"Primary header is invalid");
-	TEST_EQ(CheckHeader(mock_gpt_secondary, 1, g.drive_sectors), 0,
-		"Secondary header is valid");
+	TEST_EQ(CheckHeader(mock_gpt_primary, 0, g.drive_sectors,
+		GPT_STORED_ON_DEVICE), 1, "Primary header is invalid");
+	TEST_EQ(CheckHeader(mock_gpt_secondary, 1, g.drive_sectors,
+		GPT_STORED_ON_DEVICE), 0, "Secondary header is valid");
 	TEST_CALLS("VbExDiskRead(h, 1, 1)\n"
 		   "VbExDiskRead(h, 1023, 1)\n"
 		   "VbExDiskRead(h, 991, 32)\n");
@@ -332,10 +332,10 @@
 	Memset(mock_gpt_secondary, '\0', sizeof(*mock_gpt_secondary));
 	TEST_EQ(AllocAndReadGptData(handle, &g), 0,
 		"AllocAndRead secondary invalid");
-	TEST_EQ(CheckHeader(mock_gpt_primary, 0, g.drive_sectors), 0,
-		"Primary header is valid");
-	TEST_EQ(CheckHeader(mock_gpt_secondary, 1, g.drive_sectors), 1,
-		"Secondary header is invalid");
+	TEST_EQ(CheckHeader(mock_gpt_primary, 0, g.drive_sectors,
+		GPT_STORED_ON_DEVICE), 0, "Primary header is valid");
+	TEST_EQ(CheckHeader(mock_gpt_secondary, 1, g.drive_sectors,
+		GPT_STORED_ON_DEVICE), 1, "Secondary header is invalid");
 	TEST_CALLS("VbExDiskRead(h, 1, 1)\n"
 		   "VbExDiskRead(h, 2, 32)\n"
 		   "VbExDiskRead(h, 1023, 1)\n");
@@ -350,10 +350,10 @@
 	Memset(mock_gpt_secondary, '\0', sizeof(*mock_gpt_secondary));
 	TEST_EQ(AllocAndReadGptData(handle, &g), 1,
 		"AllocAndRead primary and secondary invalid");
-	TEST_EQ(CheckHeader(mock_gpt_primary, 0, g.drive_sectors), 1,
-		"Primary header is invalid");
-	TEST_EQ(CheckHeader(mock_gpt_secondary, 1, g.drive_sectors), 1,
-		"Secondary header is invalid");
+	TEST_EQ(CheckHeader(mock_gpt_primary, 0, g.drive_sectors,
+		GPT_STORED_ON_DEVICE), 1, "Primary header is invalid");
+	TEST_EQ(CheckHeader(mock_gpt_secondary, 1, g.drive_sectors,
+		GPT_STORED_ON_DEVICE), 1, "Secondary header is invalid");
 	TEST_CALLS("VbExDiskRead(h, 1, 1)\n"
 		   "VbExDiskRead(h, 1023, 1)\n");
 	WriteAndFreeGptData(handle, &g);
@@ -379,8 +379,8 @@
 		   "VbExDiskRead(h, 991, 32)\n"
 		   "VbExDiskWrite(h, 1, 1)\n"
 		   "VbExDiskWrite(h, 2, 32)\n");
-	TEST_EQ(CheckHeader(mock_gpt_primary, 0, g.drive_sectors), 0,
-		"Fix Primary GPT: Primary header is valid");
+	TEST_EQ(CheckHeader(mock_gpt_primary, 0, g.drive_sectors,
+		GPT_STORED_ON_DEVICE), 0, "Fix Primary GPT: Primary header is valid");
 
 	/*
 	 * Invalidate secondary GPT header and check that it can be
@@ -403,8 +403,8 @@
 		   "VbExDiskRead(h, 1023, 1)\n"
 		   "VbExDiskWrite(h, 1023, 1)\n"
 		   "VbExDiskWrite(h, 991, 32)\n");
-	TEST_EQ(CheckHeader(mock_gpt_secondary, 1, g.drive_sectors), 0,
-		"Fix Secondary GPT: Secondary header is valid");
+	TEST_EQ(CheckHeader(mock_gpt_secondary, 1, g.drive_sectors,
+		GPT_STORED_ON_DEVICE), 0, "Fix Secondary GPT: Secondary header is valid");
 
 	/* Data which is changed is written */
 	ResetMocks();