From 37f6b55a25f337f555da1dfbe585d32cd004103d Mon Sep 17 00:00:00 2001 From: Louis Yung-Chieh Lo Date: Thu, 22 Apr 2010 21:22:22 -0700 Subject: [PATCH] Add helper functions and files for gpt tests. Review URL: http://codereview.chromium.org/1729006 --- cgptlib/Makefile | 24 +++++ cgptlib/cgpt.c | 30 ++++-- cgptlib/cgpt.h | 47 ++++---- cgptlib/gpt.h | 89 ++++++++++++++++ cgptlib/tests/Makefile | 20 ++++ cgptlib/tests/cgpt_test.c | 218 +++++++++++++++++++++++++++++++++++++- cgptlib/tests/cgpt_test.h | 5 +- common/include/utility.h | 6 ++ common/utility_stub.c | 4 + 9 files changed, 409 insertions(+), 34 deletions(-) create mode 100644 cgptlib/Makefile create mode 100644 cgptlib/gpt.h create mode 100644 cgptlib/tests/Makefile diff --git a/cgptlib/Makefile b/cgptlib/Makefile new file mode 100644 index 0000000000..418163c252 --- /dev/null +++ b/cgptlib/Makefile @@ -0,0 +1,24 @@ +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +CFLAGS ?= -Wall -Werror -ansi +INCLUDES += tests +SUBDIRS = tests + +all: cgpt.a + for i in $(SUBDIRS); do \ + ( cd $$i ; $(MAKE)) ; \ + done + +.c.o: + $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +cgpt.a: cgpt.o + $(AR) rs cgpt.a $< + +clean: + for i in $(SUBDIRS); do \ + ( $(MAKE) -C $$i clean ) ; \ + done + rm -f cgpt cgpt-host *.o *~ *.a diff --git a/cgptlib/cgpt.c b/cgptlib/cgpt.c index 4504a618a5..4865473177 100644 --- a/cgptlib/cgpt.c +++ b/cgptlib/cgpt.c @@ -4,24 +4,42 @@ */ #include "cgpt.h" +#include +#include "gpt.h" +#include "utility.h" /* stub code */ static int start[] = { 34, 10034 }; -int GPTInit(GPTData_t *gpt) { +int GptInit(GptData_t *gpt) { + int valid_headers[2] = {1, 1}; + + /* check header signature */ + if (Memcmp(gpt->primary_header, GPT_HEADER_SIGNATURE, + GPT_HEADER_SIGNATURE_SIZE)) + valid_headers[0] = 0; + if (Memcmp(gpt->secondary_header, GPT_HEADER_SIGNATURE, + GPT_HEADER_SIGNATURE_SIZE)) + valid_headers[1] = 0; + + if (!valid_headers[0] && !valid_headers[1]) + return GPT_ERROR_INVALID_HEADERS; + gpt->current_kernel = 1; - return 0; + return GPT_SUCCESS; } -int GPTNextKernelEntry(GPTData_t *gpt, uint64_t *start_sector, uint64_t *size) { +int GptNextKernelEntry(GptData_t *gpt, uint64_t *start_sector, uint64_t *size) { + /* FIXME: the following code is not really code, just returns anything */ gpt->current_kernel ^= 1; if (start_sector) *start_sector = start[gpt->current_kernel]; if (size) *size = 10000; - return 0; + return GPT_SUCCESS; } -int GPTUpdateKernelEntry(GPTData_t *gpt, uint32_t update_type) { +int GptUpdateKernelEntry(GptData_t *gpt, uint32_t update_type) { + /* FIXME: the following code is not really code, just return anything */ gpt->modified |= (GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1) << gpt->current_kernel; - return 0; + return GPT_SUCCESS; } diff --git a/cgptlib/cgpt.h b/cgptlib/cgpt.h index c9cd04ddb9..ba32bb0532 100644 --- a/cgptlib/cgpt.h +++ b/cgptlib/cgpt.h @@ -9,7 +9,8 @@ #include enum { - GPT_ERROR_NO_VALID_KERNEL = 1, + GPT_SUCCESS = 0, + GPT_ERROR_NO_VALID_KERNEL, GPT_ERROR_INVALID_HEADERS, GPT_ERROR_INVALID_ENTRIES, GPT_ERROR_INVALID_SECTOR_SIZE, @@ -28,32 +29,32 @@ enum { /* The currently selected kernel partition failed validation. Mark entry as * invalid. */ -struct GPTData { - /* Fill in the following fields before calling GPTInit() */ - uint8_t *header1; /* GPT primary header, from sector 1 of disk - * (size: 512 bytes) */ - uint8_t *header2; /* GPT secondary header, from last sector of - * disk (size: 512 bytes) */ - uint8_t *entries1; /* primary GPT table, follows primary header - * (size: 16 KB) */ - uint8_t *entries2; /* secondary GPT table, precedes secondary - * header (size: 16 KB) */ - uint32_t sector_bytes; /* Size of a LBA sector, in bytes */ - uint64_t drive_sectors; /* Size of drive in LBA sectors, in sectors */ +struct GptData { + /* Fill in the following fields before calling GptInit() */ + uint8_t *primary_header; /* GPT primary header, from sector 1 of disk + * (size: 512 bytes) */ + uint8_t *secondary_header; /* GPT secondary header, from last sector of + * disk (size: 512 bytes) */ + uint8_t *primary_entries; /* primary GPT table, follows primary header + * (size: 16 KB) */ + uint8_t *secondary_entries; /* secondary GPT table, precedes secondary + * header (size: 16 KB) */ + uint32_t sector_bytes; /* Size of a LBA sector, in bytes */ + uint64_t drive_sectors; /* Size of drive in LBA sectors, in sectors */ /* Outputs */ - uint8_t modified; /* Which inputs have been modified? - * 0x01 = header1 - * 0x02 = header2 - * 0x04 = table1 - * 0x08 = table2 */ + uint8_t modified; /* Which inputs have been modified? + * 0x01 = header1 + * 0x02 = header2 + * 0x04 = table1 + * 0x08 = table2 */ /* Internal state */ - uint8_t current_kernel; // the current kernel index + uint8_t current_kernel; /* the current kernel index */ }; -typedef struct GPTData GPTData_t; +typedef struct GptData GptData_t; -int GPTInit(GPTData_t *gpt); +int GptInit(GptData_t *gpt); /* Initializes the GPT data structure's internal state. The header1, header2, * table1, table2, and drive_size fields should be filled in first. * @@ -69,7 +70,7 @@ int GPTInit(GPTData_t *gpt); * GPT_ERROR_INVALID_SECTOR_NUMBER, number of sectors in drive is invalid (too * small) */ -int GPTNextKernelEntry(GPTData_t *gpt, uint64_t *start_sector, uint64_t *size); +int GptNextKernelEntry(GptData_t *gpt, uint64_t *start_sector, uint64_t *size); /* Provides the location of the next kernel partition, in order of decreasing * priority. On return the start_sector parameter contains the LBA sector * for the start of the kernel partition, and the size parameter contains the @@ -78,7 +79,7 @@ int GPTNextKernelEntry(GPTData_t *gpt, uint64_t *start_sector, uint64_t *size); * Returns 0 if successful, else * GPT_ERROR_NO_VALID_KERNEL, no avaliable kernel, enters recovery mode */ -int GPTUpdateKernelEntry(GPTData_t *gpt, uint32_t update_type); +int GptUpdateKernelEntry(GptData_t *gpt, uint32_t update_type); /* Updates the kernel entry with the specified index, using the specified type * of update (GPT_UPDATE_ENTRY_*). * diff --git a/cgptlib/gpt.h b/cgptlib/gpt.h new file mode 100644 index 0000000000..95f0efba32 --- /dev/null +++ b/cgptlib/gpt.h @@ -0,0 +1,89 @@ +/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Defines EFI related structure. See more details in EFI 2.3 spec. + * + * To download EFI standard, please visit UEFI homepage: + * http://www.uefi.org/ + */ +#ifndef VBOOT_REFERENCE_CGPTLIB_GPT_H_ +#define VBOOT_REFERENCE_CGPTLIB_GPT_H_ + +#include + +#define GPT_HEADER_SIGNATURE "EFI PART" +#define GPT_HEADER_SIGNATURE_SIZE sizeof(GPT_HEADER_SIGNATURE) +#define GPT_HEADER_REVISION 0x00010000 + +#define GPT_ENT_TYPE_EFI \ + {{Uuid: {0xc12a7328,0xf81f,0x11d2,0xba,0x4b,{0x00,0xa0,0xc9,0x3e,0xc9,0x3b}}}} +#define GPT_ENT_TYPE_UNUSED \ + {{Uuid: {0x00000000,0x0000,0x0000,0x00,0x00,{0x00,0x00,0x00,0x00,0x00,0x00}}}} +#define GPT_ENT_TYPE_CHROMEOS_KERNEL \ + {{Uuid: {0xfe3a2a5d,0x4f32,0x41a7,0xb7,0x25,{0xac,0xcc,0x32,0x85,0xa3,0x09}}}} +#define GPT_ENT_TYPE_CHROMEOS_ROOTFS \ + {{Uuid: {0x3cb8e202,0x3b7e,0x47dd,0x8a,0x3c,{0x7f,0xf2,0xa1,0x3c,0xfc,0xec}}}} +#define GPT_ENT_TYPE_CHROMEOS_RESERVED \ + {{Uuid: {0x2e0a753d,0x9e48,0x43b0,0x83,0x37,{0xb1,0x51,0x92,0xcb,0x1b,0x5e}}}} + +#define UUID_NODE_LEN 6 +#define GUID_SIZE 16 + +/* GUID definition. + * Defined in appendix A of EFI standard. + */ +typedef struct { + union { + struct { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_high_and_version; + uint8_t clock_seq_high_and_reserved; + uint8_t clock_seq_low; + uint8_t node[UUID_NODE_LEN]; + } Uuid; + uint8_t raw[GUID_SIZE]; + }; +} __attribute__((packed)) Guid; + +/* GPT header defines how many partitions exist on a drive and sectors managed. + * For every drive device, there are 2 headers, primary and secondary. + * Most of fields are duplicated except my_lba and entries_lba. + * + * You may find more details in chapter 5 of EFI standard. + */ +typedef struct { + char signature[8]; + uint32_t revision; + uint32_t size; + uint32_t header_crc32; + uint32_t reserved; + uint64_t my_lba; + uint64_t alternate_lba; + uint64_t first_usable_lba; + uint64_t last_usable_lba; + Guid disk_uuid; + + uint64_t entries_lba; + uint32_t number_of_entries; + uint32_t size_of_entry; + uint32_t entries_crc32; + uint32_t padding; /* since header size must be a multiple of 8, pad here. */ +} GptHeader; + +/* GPT partition entry defines the starting and ending LBAs of a partition. + * It also contains the unique GUID, type, and attribute bits. + * + * You may find more details in chapter 5 of EFI standard. + */ +typedef struct { + Guid type; + Guid unique; + uint64_t starting_lba; + uint64_t ending_lba; + uint64_t attributes; + uint16_t name[36]; /* UTF-16 encoded partition name */ +} GptEntry; + +#endif /* VBOOT_REFERENCE_CGPTLIB_GPT_H_ */ diff --git a/cgptlib/tests/Makefile b/cgptlib/tests/Makefile new file mode 100644 index 0000000000..60871fa882 --- /dev/null +++ b/cgptlib/tests/Makefile @@ -0,0 +1,20 @@ +# Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +CC ?= cc +CFLAGS ?= -Wall -DNDEBUG -Werror -ansi +LIBS = ../cgpt.a ../../common/libcommon.a +OBJS = cgpt_test.o +OUT = cgpt_test + +all: $(OUT) + +$(OUT): $(OBJS) $(LIBS) + $(CC) -o $@ $^ + +.c.o: + $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@ + +clean: + rm -rf $(OUT) $(OBJS) diff --git a/cgptlib/tests/cgpt_test.c b/cgptlib/tests/cgpt_test.c index b5b26e70f9..a451838109 100644 --- a/cgptlib/tests/cgpt_test.c +++ b/cgptlib/tests/cgpt_test.c @@ -3,12 +3,219 @@ * found in the LICENSE file. */ -#include "cgpt.h" #include "cgpt_test.h" +#include +#include "cgpt.h" +#include "gpt.h" +#include "utility.h" + +/* Testing partition layout (sector_bytes=512) + * + * LBA Size Usage + * 0 1 PMBR + * 1 1 primary partition header + * 2 32 primary partition entries (128B * 128) + * 34 100 kernel A + * 134 100 kernel B + * 234 100 root A + * 334 100 root B + * 434 32 secondary partition entries + * 466 1 secondary partition header + * 467 + */ +#define DEFAULT_SECTOR_SIZE 512 +#define MAX_SECTOR_SIZE 4096 +#define DEFAULT_DRIVE_SECTORS 467 +#define PARTITION_ENTRIES_SIZE (16*1024) #define TEST_CASE(func) #func, func typedef int (*test_func)(void); +/* NOT A REAL CRC32, it is fake before I call real one . FIXME */ +uint32_t CalculateCrc32(const uint8_t *start, size_t len) { + uint32_t buf = 0; + int i; + for (i = 0; i < len; i += 4, len -= 4) { + buf ^= *(uint32_t*)&start[i]; + } + if (len >= 3) buf ^= start[i-2] << 16; + if (len >= 2) buf ^= start[i-3] << 8; + if (len >= 1) buf ^= start[i-4]; + return buf; +} + +/* Given a GptData pointer, first re-calculate entries CRC32 value, + * then reset header CRC32 value to 0, and calculate header CRC32 value. + * Both primary and secondary are updated. */ +void RefreshCrc32(struct GptData *gpt) { + GptHeader *header, *header2; + GptEntry *entries, *entries2; + + header = (GptHeader*)gpt->primary_header; + entries = (GptEntry*)gpt->primary_entries; + header2 = (GptHeader*)gpt->secondary_header; + entries2 = (GptEntry*)gpt->secondary_entries; + + header->entries_crc32 = CalculateCrc32((uint8_t*)entries, + sizeof(GptEntry)); + header->header_crc32 = 0; + header->header_crc32 = CalculateCrc32((uint8_t*)header, + header->size); + header2->entries_crc32 = CalculateCrc32((uint8_t*)entries2, + sizeof(GptEntry)); + header2->header_crc32 = 0; + header2->header_crc32 = CalculateCrc32((uint8_t*)header2, + header2->size); +} + +/* Returns a pointer to a static GptData instance (no free is required). + * All fields are zero except 4 pointers linking to header and entries. + * All content of headers and entries are zero. */ +struct GptData* GetAClearGptData() { + static GptData_t gpt; + static uint8_t primary_header[MAX_SECTOR_SIZE]; + static uint8_t primary_entries[PARTITION_ENTRIES_SIZE]; + static uint8_t secondary_header[MAX_SECTOR_SIZE]; + static uint8_t secondary_entries[PARTITION_ENTRIES_SIZE]; + + Memset(&gpt, 0, sizeof(gpt)); + Memset(&primary_header, 0, sizeof(primary_header)); + Memset(&primary_entries, 0, sizeof(primary_entries)); + Memset(&secondary_header, 0, sizeof(secondary_header)); + Memset(&secondary_entries, 0, sizeof(secondary_entries)); + + gpt.primary_header = primary_header; + gpt.primary_entries = primary_entries; + gpt.secondary_header = secondary_header; + gpt.secondary_entries = secondary_entries; + + return &gpt; +} + +/* Fills in most of fields and creates the layout described in the top of this + * file. */ +struct GptData* +BuildTestGptData(uint32_t sector_bytes) { + GptData_t *gpt; + GptHeader *header, *header2; + GptEntry *entries, *entries2; + Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; + + gpt = GetAClearGptData(); + gpt->sector_bytes = sector_bytes; + gpt->drive_sectors = DEFAULT_DRIVE_SECTORS; + + /* build primary */ + header = (GptHeader*)gpt->primary_header; + entries = (GptEntry*)gpt->primary_entries; + Memcpy(header->signature, GPT_HEADER_SIGNATURE, sizeof(GPT_HEADER_SIGNATURE)); + header->revision = GPT_HEADER_REVISION; + header->size = sizeof(GptHeader) - sizeof(header->padding); + header->my_lba = 1; + header->first_usable_lba = 34; + header->last_usable_lba = DEFAULT_DRIVE_SECTORS - 1 - 32 - 1; /* 433 */ + header->entries_lba = 2; + header->number_of_entries = 128; /* 512B / 128B * 32sectors = 128 entries */ + header->size_of_entry = 128; /* bytes */ + Memcpy(&entries[0].type, &chromeos_kernel, sizeof(chromeos_kernel)); + entries[0].starting_lba = 34; + entries[0].ending_lba = 133; + Memcpy(&entries[1].type, &chromeos_kernel, sizeof(chromeos_kernel)); + entries[1].starting_lba = 134; + entries[1].ending_lba = 233; + Memcpy(&entries[2].type, &chromeos_kernel, sizeof(chromeos_kernel)); + entries[2].starting_lba = 234; + entries[2].ending_lba = 333; + Memcpy(&entries[3].type, &chromeos_kernel, sizeof(chromeos_kernel)); + entries[3].starting_lba = 334; + entries[3].ending_lba = 433; + + /* build secondary */ + header2 = (GptHeader*)gpt->secondary_header; + entries2 = (GptEntry*)gpt->secondary_entries; + Memcpy(header2, header, sizeof(header)); + Memcpy(entries2, entries, sizeof(entries)); + header2->my_lba = DEFAULT_DRIVE_SECTORS - 1; /* 466 */ + header2->entries_lba = DEFAULT_DRIVE_SECTORS - 1 - 32; /* 434 */ + + RefreshCrc32(gpt); + return gpt; +} + +/* Dumps memory starting from [vp] with [len] bytes. + * Prints [memo] if not NULL. Example output: + * + * 00 01 02 03 04 05 06 07 - 08 09 0a 0b 0c 0d 0e 0f + * 10 11 12 13 14 15 16 17 - 18 19 1a 1b 1c 1d 1e 1f + * ... + */ +static void dump(void *vp, int len, char* memo) { + uint8_t *start = vp; + int i; + if (memo) printf("--[%s]----------\n", memo); + for (i = 0; i < len; ++i) { + printf("%02x%s", start[i], + (!(~i & 15) ? "\n" : + !(~i & 7) ? " - ": " ")); + } + if (i&15) printf("\n"); +} + +/* More formatted dump with GptData. */ +void DumpGptData(struct GptData *gpt) { + printf("DumpGptData(%p)...\n", gpt); + dump(gpt, sizeof(gpt), NULL); + dump(gpt->primary_header, sizeof(GptHeader), "Primary header"); + dump(gpt->primary_entries, sizeof(GptEntry) * 8, "Primary entries"); + dump(gpt->secondary_header, sizeof(GptHeader), "Secondary header"); + dump(gpt->secondary_entries, sizeof(GptEntry) * 8, + "Secondary entries"); +} + +/* Tests if signature ("EFI PART") is checked. */ +int SignatureTest() { + int i; + GptData_t *gpt; + GptHeader *primary_header, *secondary_header; + + gpt = BuildTestGptData(DEFAULT_SECTOR_SIZE); + primary_header = (GptHeader*)gpt->primary_header; + secondary_header = (GptHeader*)gpt->secondary_header; + + EXPECT(GPT_SUCCESS == GptInit(gpt)); + + /* change every char in signature of primary. Secondary is still valid. */ + for (i = 0; i < 8; ++i) { + gpt->primary_header[i] ^= 0xff; + RefreshCrc32(gpt); + EXPECT(GPT_SUCCESS == GptInit(gpt)); + gpt->primary_header[i] ^= 0xff; + RefreshCrc32(gpt); + } + + /* change every char in signature of secondary. Primary is still valid. */ + for (i = 0; i < 8; ++i) { + gpt->secondary_header[i] ^= 0xff; + RefreshCrc32(gpt); + EXPECT(GPT_SUCCESS == GptInit(gpt)); + gpt->secondary_header[i] ^= 0xff; + RefreshCrc32(gpt); + } + + /* change every char in signature of primary and secondary. Expect fail. */ + for (i = 0; i < 8; ++i) { + gpt->primary_header[i] ^= 0xff; + gpt->secondary_header[i] ^= 0xff; + RefreshCrc32(gpt); + EXPECT(GPT_ERROR_INVALID_HEADERS == GptInit(gpt)); + gpt->primary_header[i] ^= 0xff; + gpt->secondary_header[i] ^= 0xff; + RefreshCrc32(gpt); + } + + return TEST_OK; +} + /* Tests if header CRC in two copies are calculated. */ int HeaderCrcTest() { return TEST_FAIL; @@ -45,7 +252,7 @@ int FirstUsableLbaAndLastUsableLbaTest() { return TEST_FAIL; } -/* Tests if GPTInit() handles non-identical partition entries well. +/* Tests if GptInit() handles non-identical partition entries well. * Two copies of partition table entries must be identical. If not, we trust the * primary table entries, and mark secondary as modified (see Caller's write- * back order below). */ @@ -53,7 +260,7 @@ int IdenticalEntriesTest() { return TEST_FAIL; } -/* Tests if GPTInit() handles non-identical headers well. +/* Tests if GptInit() handles non-identical headers well. * Two partition headers must be identical. If not, we trust the primary * partition header, and mark secondary as modified (see Caller's write-back * order below). */ @@ -84,7 +291,7 @@ int NoOverlappedPartitionTest() { return TEST_FAIL; } -/* Tests if GPTNextKernelEntry() can survive in different corrupt header/entries +/* Tests if GptNextKernelEntry() can survive in different corrupt header/entries * combinations, like: * primary GPT header - valid * primary partition table - invalid @@ -102,6 +309,8 @@ int main(int argc, char *argv[]) { test_func fp; int retval; } test_cases[] = { + { TEST_CASE(SignatureTest), }, +#if 0 { TEST_CASE(HeaderCrcTest), }, { TEST_CASE(MyLbaTest), }, { TEST_CASE(SizeOfPartitionEntryTest), }, @@ -114,6 +323,7 @@ int main(int argc, char *argv[]) { { TEST_CASE(ValidEntryTest), }, { TEST_CASE(NoOverlappedPartitionTest), }, { TEST_CASE(CorruptCombinationTest), }, +#endif }; for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) { diff --git a/cgptlib/tests/cgpt_test.h b/cgptlib/tests/cgpt_test.h index 0dcb637e82..4a8b115ff9 100644 --- a/cgptlib/tests/cgpt_test.h +++ b/cgptlib/tests/cgpt_test.h @@ -7,7 +7,10 @@ #include -#define TEST_FAIL -1 +enum { + TEST_FAIL = -1, + TEST_OK = 0, +}; /* ANSI Color coding sequences. */ #define COL_GREEN "\e[1;32m" diff --git a/common/include/utility.h b/common/include/utility.h index 8619cd2198..a5df8fa6ba 100644 --- a/common/include/utility.h +++ b/common/include/utility.h @@ -40,6 +40,12 @@ void* Malloc(size_t size); /* Free memory pointed by [ptr] previously allocated by Malloc(). */ void Free(void* ptr); +/* Compare [n] bytes in [src1] and [src2] + * Returns an integer less than, equal to, or greater than zero if the first [n] + * bytes of [src1] is found, respectively, to be less than, to match, or be + * greater than the first n bytes of [src2]. */ +int Memcmp(const void* src1, const void* src2, size_t n); + /* Copy [n] bytes from [src] to [dest]. */ void* Memcpy(void* dest, const void* src, size_t n); diff --git a/common/utility_stub.c b/common/utility_stub.c index d2244c797b..66132703ea 100644 --- a/common/utility_stub.c +++ b/common/utility_stub.c @@ -42,6 +42,10 @@ void Free(void* ptr) { free(ptr); } +int Memcmp(const void* src1, const void* src2, size_t n) { + return memcmp(src1, src2, n); +} + void* Memcpy(void* dest, const void* src, size_t n) { return memcpy(dest, src, n); }