diff options
-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(); |