diff options
author | Nam T. Nguyen <namnguyen@chromium.org> | 2014-10-24 13:20:39 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-11-13 18:29:09 +0000 |
commit | 6ee52d9a929d00e871e7316240b54f381146fbc6 (patch) | |
tree | ca403414469d9364d144a67e63f8f439874802e3 | |
parent | 43e0a9ed6c0b332631442fcf581e7456d62e4532 (diff) | |
download | vboot-6ee52d9a929d00e871e7316240b54f381146fbc6.tar.gz |
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>
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | cgpt/cgpt.h | 32 | ||||
-rw-r--r-- | cgpt/cgpt_common.c | 72 | ||||
-rw-r--r-- | cgpt/cgpt_create.c | 35 | ||||
-rw-r--r-- | cgpt/cgpt_show.c | 2 | ||||
-rw-r--r-- | cgpt/drive.c | 305 | ||||
-rw-r--r-- | cgpt/drive.h | 29 | ||||
-rw-r--r-- | firmware/include/gpt_misc.h | 26 | ||||
-rw-r--r-- | firmware/lib/cgptlib/cgptlib_internal.c | 35 | ||||
-rw-r--r-- | firmware/lib/cgptlib/include/cgptlib_internal.h | 3 | ||||
-rw-r--r-- | firmware/lib/gpt_misc.c | 14 | ||||
-rw-r--r-- | firmware/lib/vboot_kernel.c | 3 | ||||
-rw-r--r-- | tests/cgptlib_test.c | 139 | ||||
-rwxr-xr-x | tests/run_cgpt_tests.sh | 6 | ||||
-rw-r--r-- | tests/vboot_kernel_tests.c | 34 |
15 files changed, 642 insertions, 100 deletions
@@ -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(); |