summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile7
-rw-r--r--cgpt/cgpt.h32
-rw-r--r--cgpt/cgpt_common.c72
-rw-r--r--cgpt/cgpt_create.c35
-rw-r--r--cgpt/cgpt_show.c2
-rw-r--r--cgpt/drive.c305
-rw-r--r--cgpt/drive.h29
-rw-r--r--firmware/include/gpt_misc.h26
-rw-r--r--firmware/lib/cgptlib/cgptlib_internal.c35
-rw-r--r--firmware/lib/cgptlib/include/cgptlib_internal.h3
-rw-r--r--firmware/lib/gpt_misc.c14
-rw-r--r--firmware/lib/vboot_kernel.c3
-rw-r--r--tests/cgptlib_test.c139
-rwxr-xr-xtests/run_cgpt_tests.sh6
-rw-r--r--tests/vboot_kernel_tests.c34
15 files changed, 642 insertions, 100 deletions
diff --git a/Makefile b/Makefile
index 146c042d..87ef8077 100644
--- a/Makefile
+++ b/Makefile
@@ -360,6 +360,7 @@ UTILLIB_SRCS = \
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 @@ HOSTLIB_SRCS = \
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 @@ HOSTLIB_SRCS = \
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 @@ TINYHOSTLIB_SRCS = \
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 @@ TINYHOSTLIB_SRCS = \
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 aa47314d..ea459cb1 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 @@ struct legacy_partition {
uint32_t num_sect;
} __attribute__((packed));
-
// syslinux uses this format:
struct pmbr {
uint8_t bootcode[424];
@@ -43,12 +43,33 @@ void PMBRToStr(struct pmbr *pmbr, char *str, unsigned int buflen);
// 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 @@ struct nand_layout {
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 @@ int GenerateGuid(Guid *newguid);
// 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 3900cf33..43312fe6 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 @@ int Load(struct drive *drive, uint8_t **buf,
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 @@ int Load(struct drive *drive, uint8_t **buf,
*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 @@ error_free:
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 ReadPMBR(struct drive *drive) {
}
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 @@ int Save(struct drive *drive, const uint8_t *buf,
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 @@ static int GptLoad(struct drive *drive, uint32_t sector_bytes) {
}
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 @@ static int GptLoad(struct drive *drive, uint32_t sector_bytes) {
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 @@ static int GptLoad(struct drive *drive, uint32_t sector_bytes) {
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 @@ static int GptSave(struct drive *drive) {
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 @@ int DriveOpen(const char *drive_path, struct drive *drive, int mode) {
// 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 @@ int DriveOpen(const char *drive_path, struct drive *drive, int mode) {
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 @@ int DriveClose(struct drive *drive, int update_as_needed) {
// 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 @@ uint8_t RepairHeader(GptData *gpt, const uint32_t valid_headers) {
}
} 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 7d3c0595..6a21a8b0 100644
--- a/cgpt/cgpt_create.c
+++ b/cgpt/cgpt_create.c
@@ -44,17 +44,40 @@ static int GptCreate(struct drive *drive, CgptCreateParams *params) {
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 4788bfd9..5b7974f7 100644
--- a/cgpt/cgpt_show.c
+++ b/cgpt/cgpt_show.c
@@ -445,7 +445,7 @@ static int GptShow(struct drive *drive, CgptShowParams *params) {
}
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 00000000..db6601d1
--- /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 00000000..d03bfe74
--- /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 4d91bd8c..53b30347 100644
--- a/firmware/include/gpt_misc.h
+++ b/firmware/include/gpt_misc.h
@@ -57,6 +57,24 @@ enum {
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 @@ typedef struct {
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 @@ typedef struct {
* 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 4eea03df..e9f27c96 100644
--- a/firmware/lib/cgptlib/cgptlib_internal.c
+++ b/firmware/lib/cgptlib/cgptlib_internal.c
@@ -20,6 +20,16 @@ int CheckParameters(GptData *gpt)
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 @@ uint32_t HeaderCrc(GptHeader *h)
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 @@ int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors)
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 @@ int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors)
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 @@ int GptSanityCheck(GptData *gpt)
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 e9e63cad..4a52420c 100644
--- a/firmware/lib/cgptlib/include/cgptlib_internal.h
+++ b/firmware/lib/cgptlib/include/cgptlib_internal.h
@@ -90,7 +90,8 @@ int CheckParameters(GptData* gpt);
*
* 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 d00d6b2e..499cc9e7 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 @@ int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata)
/* 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 @@ int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata)
}
/* 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 @@ int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata)
}
}
- 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 @@ int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata)
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 b2cb8172..62e62967 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -116,6 +116,9 @@ VbError_t LoadKernel(LoadKernelParams *params, VbCommonParams *cparams)
/* 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 363ecc8a..12b366ad 100644
--- a/tests/cgptlib_test.c
+++ b/tests/cgptlib_test.c
@@ -164,7 +164,7 @@ static void BuildTestGptData(GptData *gpt)
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 @@ static int ParameterTests(void)
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 @@ static int SignatureTest(void)
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 @@ static int RevisionTest(void)
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 @@ static int SizeTest(void)
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 @@ static int CrcFieldTest(void)
/* 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 @@ static int ReservedFieldsTest(void)
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 @@ static int ReservedFieldsTest(void)
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 @@ static int SizeOfPartitionEntryTest(void) {
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 @@ static int NumberOfPartitionEntriesTest(void)
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 @@ static int MyLbaTest(void)
/* 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 @@ static int MyLbaTest(void)
* 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 @@ static int MyLbaTest(void)
/* 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 @@ static int FirstUsableLbaAndLastUsableLbaTest(void)
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 @@ static int MtdFtsTest() {
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 @@ int main(int argc, char *argv[])
{ 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 847c0813..95840fbb 100755
--- a/tests/run_cgpt_tests.sh
+++ b/tests/run_cgpt_tests.sh
@@ -21,6 +21,12 @@ DIR="${TEST_DIR}/cgpt_test_dir"
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 0c26d212..05035f28 100644
--- a/tests/vboot_kernel_tests.c
+++ b/tests/vboot_kernel_tests.c
@@ -289,7 +289,7 @@ static void ReadWriteGptTest(void)
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 @@ static void ReadWriteGptTest(void)
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 @@ static void ReadWriteGptTest(void)
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 @@ static void ReadWriteGptTest(void)
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 @@ static void ReadWriteGptTest(void)
"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 @@ static void ReadWriteGptTest(void)
"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();