diff options
-rw-r--r-- | Makefile | 44 | ||||
-rw-r--r-- | firmware/2lib/2common.c | 95 | ||||
-rw-r--r-- | firmware/2lib/include/2common.h | 120 | ||||
-rw-r--r-- | firmware/2lib/include/2return_codes.h | 62 | ||||
-rw-r--r-- | firmware/2lib/include/2sysincludes.h | 27 | ||||
-rw-r--r-- | tests/vb2_common_tests.c | 102 |
6 files changed, 445 insertions, 5 deletions
@@ -221,7 +221,8 @@ INCLUDES += \ -Ifirmware/lib/include \ -Ifirmware/lib/cgptlib/include \ -Ifirmware/lib/cryptolib/include \ - -Ifirmware/lib/tpm_lite/include + -Ifirmware/lib/tpm_lite/include \ + -Ifirmware/2lib/include # If we're not building for a specific target, just stub out things like the # TPM commands and various external functions that are provided by the BIOS. @@ -234,6 +235,11 @@ endif # Firmware library. TODO: Do we still need to export this? FWLIB = ${BUILD}/vboot_fw.a +# Smaller firmware library. TODO: Do we still need to export this? +ifneq (${VBOOT2},) +FWLIB2 = ${BUILD}/vboot_fw2.a +endif + # Firmware library sources needed by VbInit() call VBINIT_SRCS = \ firmware/lib/crc8.c \ @@ -271,6 +277,10 @@ VBSLK_SRCS = \ firmware/lib/vboot_kernel.c \ firmware/lib/region-kernel.c \ +# Firmware library source needed for smaller library 2 +FWLIB2_SRCS = \ + firmware/2lib/2common.c \ + # Support real TPM unless BIOS sets MOCK_TPM ifeq (${MOCK_TPM},) VBINIT_SRCS += \ @@ -313,8 +323,11 @@ VBSF_OBJS = ${VBSF_SRCS:%.c=${BUILD}/%.o} FWLIB_OBJS = ${FWLIB_SRCS:%.c=${BUILD}/%.o} -ALL_OBJS += ${FWLIB_OBJS} ${VBINIT_OBJS} ${VBSF_OBJS} +ifneq (${VBOOT2},) +FWLIB2_OBJS = ${FWLIB2_SRCS:%.c=${BUILD}/%.o} +endif +ALL_OBJS += ${FWLIB_OBJS} ${FWLIB2_OBJS} ${VBINIT_OBJS} ${VBSF_OBJS} # Library to build the utilities. "HOST" mostly means "userspace". HOSTLIB = ${BUILD}/libvboot_host.a @@ -543,6 +556,12 @@ ifdef REGION_READ TEST_NAMES += tests/vboot_region_tests endif +ifneq (${VBOOT2},) +TEST_NAMES += \ + tests/vb2_common_tests \ + +endif + # TODO: port these tests to new API, if not already eqivalent # functionality in other tests. These don't even compile at present. # @@ -598,7 +617,8 @@ _dir_create := $(foreach d, \ # Default target. .PHONY: all -all: fwlib $(if ${FIRMWARE_ARCH},,host_stuff) $(if ${COV},coverage) +all: fwlib $(if ${VBOOT2},fwlib2) $(if ${FIRMWARE_ARCH},,host_stuff) \ + $(if ${COV},coverage) # Host targets .PHONY: host_stuff @@ -639,6 +659,7 @@ ${FWLIB_OBJS}: CFLAGS += -DTPM_BLOCKING_CONTINUESELFTEST ifeq (${FIRMWARE_ARCH},i386) # Unrolling loops in cryptolib makes it faster ${FWLIB_OBJS}: CFLAGS += -DUNROLL_LOOPS +${FWLIB2_OBJS}: CFLAGS += -DUNROLL_LOOPS # Workaround for coreboot on x86, which will power off asynchronously # without giving us a chance to react. This is not an example of the Right @@ -682,12 +703,21 @@ fwlinktest: ${FWLIB} \ .PHONY: fwlib fwlib: $(if ${FIRMWARE_ARCH},${FWLIB},fwlinktest) +.PHONY: fwlib2 +fwlib2: ${FWLIB2} + ${FWLIB}: ${FWLIB_OBJS} @$(PRINTF) " RM $(subst ${BUILD}/,,$@)\n" ${Q}rm -f $@ @$(PRINTF) " AR $(subst ${BUILD}/,,$@)\n" ${Q}ar qc $@ $^ +${FWLIB2}: ${FWLIB2_OBJS} + @$(PRINTF) " RM $(subst ${BUILD}/,,$@)\n" + ${Q}rm -f $@ + @$(PRINTF) " AR $(subst ${BUILD}/,,$@)\n" + ${Q}ar qc $@ $^ + # ---------------------------------------------------------------------------- # Host library @@ -706,7 +736,7 @@ ${BUILD}/host/% ${HOSTLIB}: INCLUDES += \ -Ihost/lib/include # TODO: better way to make .a than duplicating this recipe each time? -${HOSTLIB}: ${HOSTLIB_OBJS} ${FWLIB_OBJS} +${HOSTLIB}: ${HOSTLIB_OBJS} ${FWLIB_OBJS} $(if ${VBOOT2},${FWLIB2_OBJS}) @$(PRINTF) " RM $(subst ${BUILD}/,,$@)\n" ${Q}rm -f $@ @$(PRINTF) " AR $(subst ${BUILD}/,,$@)\n" @@ -957,7 +987,7 @@ ALL_OBJS += ${BUILD}/tests/tpm_lite/tlcl_tests.o # Frequently-run tests .PHONY: test_targets -test_targets:: runcgpttests runmisctests +test_targets:: runcgpttests runmisctests $(if ${VBOOT2},run2tests) ifeq (${MINIMAL},) # Bitmap utility isn't compiled for minimal variant @@ -1044,6 +1074,10 @@ runmisctests: test_setup ${RUNTEST} ${BUILD_RUN}/tests/vboot_kernel_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_nvstorage_test +.PHONY: run2tests +run2tests: test_setup + ${RUNTEST} ${BUILD_RUN}/tests/vb2_common_tests + .PHONY: runfutiltests runfutiltests: override DESTDIR = ${TEST_INSTALL_DIR} runfutiltests: test_setup install diff --git a/firmware/2lib/2common.c b/firmware/2lib/2common.c new file mode 100644 index 00000000..9849b22b --- /dev/null +++ b/firmware/2lib/2common.c @@ -0,0 +1,95 @@ +/* Copyright (c) 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. + * + * Common functions between firmware and kernel verified boot. + * (Firmware portion) + */ + +#include "2sysincludes.h" +#include "2common.h" + +int vb2_align(uint8_t **ptr, uint32_t *size, uint32_t align, uint32_t want_size) +{ + uintptr_t p = (uintptr_t)*ptr; + uintptr_t offs = p & (align - 1); + + if (offs) { + offs = align - offs; + + if (*size < offs) + return VB2_ERROR_BUFFER_TOO_SMALL; + + *ptr += offs; + *size -= offs; + } + + if (*size < want_size) + return VB2_ERROR_BUFFER_TOO_SMALL; + + return VB2_SUCCESS; +} + +void vb2_workbuf_init(struct vb2_workbuf *wb, uint8_t *buf, uint32_t size) +{ + wb->buf = buf; + wb->size = size; + + /* Align the buffer so allocations will be aligned */ + if (vb2_align(&wb->buf, &wb->size, VB2_WORKBUF_ALIGN, 0)) + wb->size = 0; +} + +/** + * Round up a number to a multiple of VB2_WORKBUF_ALIGN + * + * @param v Number to round up + * @return The number, rounded up. + */ +static __inline uint32_t wb_round_up(uint32_t v) +{ + return (v + VB2_WORKBUF_ALIGN - 1) & ~(VB2_WORKBUF_ALIGN - 1); +} + +void *vb2_workbuf_alloc(struct vb2_workbuf *wb, uint32_t size) +{ + uint8_t *ptr = wb->buf; + + /* Round up size to work buffer alignment */ + size = wb_round_up(size); + + if (size > wb->size) + return NULL; + + wb->buf += size; + wb->size -= size; + + return ptr; +} + +void *vb2_workbuf_realloc(struct vb2_workbuf *wb, + uint32_t oldsize, + uint32_t newsize) +{ + /* + * Just free and allocate to update the size. No need to move/copy + * memory, since the new pointer is guaranteed to be the same as the + * old one. The new allocation can fail, if the new size is too big. + */ + vb2_workbuf_free(wb, oldsize); + return vb2_workbuf_alloc(wb, newsize); +} + +void vb2_workbuf_free(struct vb2_workbuf *wb, uint32_t size) +{ + /* Round up size to work buffer alignment */ + size = wb_round_up(size); + + wb->buf -= size; + wb->size += size; +} + +ptrdiff_t vb2_offset_of(const void *base, const void *ptr) +{ + return (uintptr_t)ptr - (uintptr_t)base; +} diff --git a/firmware/2lib/include/2common.h b/firmware/2lib/include/2common.h new file mode 100644 index 00000000..3724354b --- /dev/null +++ b/firmware/2lib/include/2common.h @@ -0,0 +1,120 @@ +/* Copyright (c) 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. + * + * Common functions between firmware and kernel verified boot. + */ + +#ifndef VBOOT_REFERENCE_VBOOT_2COMMON_H_ +#define VBOOT_REFERENCE_VBOOT_2COMMON_H_ + +#include "2return_codes.h" + +struct vb2_public_key; + +/* + * Return the greater of A and B. This is used in macros which calculate the + * required buffer size, so can't be turned into a static inline function. + */ +#ifndef VB2_MAX +#define VB2_MAX(A, B) ((A) > (B) ? (A) : (B)) +#endif + +/* + * Debug output. Defaults to printf(), but can be overridden on a per-platform + * basis. + */ +#if defined(VBOOT_DEBUG) && !defined(VB2_DEBUG) +#define VB2_DEBUG(format, args...) printf(format, ## args) +#else +#define VB2_DEBUG(format, args...) +#endif + +/* Alignment for work buffer pointers/allocations */ +#define VB2_WORKBUF_ALIGN 8 + +/* Work buffer */ +struct vb2_workbuf { + uint8_t *buf; + uint32_t size; +}; + +/** + * Initialize a work buffer. + * + * @param wb Work buffer to init + * @param buf Pointer to work buffer data + * @param size Size of work buffer data in bytes + */ +void vb2_workbuf_init(struct vb2_workbuf *wb, uint8_t *buf, uint32_t size); + +/** + * Allocate space in a work buffer. + * + * Note that the returned buffer will always be aligned to VB2_WORKBUF_ALIGN. + * + * The work buffer acts like a stack, and detailed tracking of allocs and frees + * is not done. The caller must track the size of each allocation and free via + * vb2_workbuf_free() in the reverse order they were allocated. + * + * @param wb Work buffer + * @param size Requested size in bytes + * @return A pointer to the allocated space, or NULL if error. + */ +void *vb2_workbuf_alloc(struct vb2_workbuf *wb, uint32_t size); + +/** + * Reallocate space in a work buffer. + * + * Note that the returned buffer will always be aligned to VB2_WORKBUF_ALIGN. + * The work buffer acts like a stack, so this must only be done to the most + * recently allocated buffer. + * + * @param wb Work buffer + * @param oldsize Old allocation size in bytes + * @param newsize Requested size in bytes + * @return A pointer to the allocated space, or NULL if error. + */ +void *vb2_workbuf_realloc(struct vb2_workbuf *wb, + uint32_t oldsize, + uint32_t newsize); + +/** + * Free the preceding allocation. + * + * Note that the work buffer acts like a stack, and detailed tracking of + * allocs and frees is not done. The caller must track the size of each + * allocation and free them in reverse order. + * + * @param wb Work buffer + * @param size Size of data to free + */ +void vb2_workbuf_free(struct vb2_workbuf *wb, uint32_t size); + +/* Check if a pointer is aligned on an align-byte boundary */ +#define vb_aligned(ptr, align) (!(((size_t)(ptr)) & ((align) - 1))) + +/** + * Align a buffer and check its size. + * + * @param **ptr Pointer to pointer to align + * @param *size Points to size of buffer pointed to by *ptr + * @param align Required alignment (must be power of 2) + * @param want_size Required size + * @return VB2_SUCCESS, or non-zero if error. + */ +int vb2_align(uint8_t **ptr, + uint32_t *size, + uint32_t align, + uint32_t want_size); + +/** + * Return offset of ptr from base. + * + * @param base Base pointer + * @param ptr Pointer at some offset from base + * @return The offset of ptr from base. + */ +ptrdiff_t vb2_offset_of(const void *base, const void *ptr); + +#endif /* VBOOT_REFERENCE_VBOOT_2COMMON_H_ */ diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h new file mode 100644 index 00000000..73a37b5e --- /dev/null +++ b/firmware/2lib/include/2return_codes.h @@ -0,0 +1,62 @@ +/* Copyright (c) 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_2_RETURN_CODES_H_ +#define VBOOT_2_RETURN_CODES_H_ + +/* + * Return codes from verified boot functions. + * + * TODO: Go through code and replace VB2_ERROR_UNKNOWN with more specific + * error codes, and make the existing codes more consistent and useful. + */ +enum vb2_return_code { + /* Success - no error */ + VB2_SUCCESS = 0, + + /* Unknown / unspecified error */ + VB2_ERROR_UNKNOWN = 0x10000, + + /* Work buffer too small */ + VB2_ERROR_WORKBUF_TOO_SMALL, + + /* Buffer too small (other than the work buffer) */ + VB2_ERROR_BUFFER_TOO_SMALL, + + /* Buffer unaligned */ + VB2_ERROR_BUFFER_UNALIGNED, + + /* Bad GBB header */ + VB2_ERROR_BAD_GBB_HEADER, + + /* Bad algorithm - unknown, or unsupported */ + VB2_ERROR_BAD_ALGORITHM, + + /* Signature check failed */ + VB2_ERROR_BAD_SIGNATURE, + + /* Bad secure data */ + VB2_ERROR_BAD_SECDATA, + + /* Bad key */ + VB2_ERROR_BAD_KEY, + + /* Bad keyblock */ + VB2_ERROR_BAD_KEYBLOCK, + + /* Bad preamble */ + VB2_ERROR_BAD_PREAMBLE, + + /* Bad firmware keyblock version (out of range, or rollback) */ + VB2_ERROR_FW_KEYBLOCK_VERSION, + + /* Bad firmware version (out of range, or rollback) */ + VB2_ERROR_FW_VERSION, + + /* Bad hash tag */ + VB2_ERROR_BAD_TAG, +}; + +#endif /* VBOOT_2_RETURN_CODES_H_ */ diff --git a/firmware/2lib/include/2sysincludes.h b/firmware/2lib/include/2sysincludes.h new file mode 100644 index 00000000..4c9e66c6 --- /dev/null +++ b/firmware/2lib/include/2sysincludes.h @@ -0,0 +1,27 @@ +/* Copyright (c) 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. + */ + +/* + * System includes for vboot reference library. With few exceptions, this is + * the ONLY place in firmware/ where system headers may be included via + * #include <...>, so that there's only one place that needs to be fixed up for + * platforms which don't have all the system includes. + */ + +#ifndef VBOOT_REFERENCE_2_SYSINCLUDES_H_ +#define VBOOT_REFERENCE_2_SYSINCLUDES_H_ + +#include <inttypes.h> /* For PRIu64 */ +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#if defined(HAVE_ENDIAN_H) && defined(HAVE_LITTLE_ENDIAN) +#include <byteswap.h> +#include <memory.h> +#endif + +#endif /* VBOOT_REFERENCE_2_SYSINCLUDES_H_ */ diff --git a/tests/vb2_common_tests.c b/tests/vb2_common_tests.c new file mode 100644 index 00000000..123de811 --- /dev/null +++ b/tests/vb2_common_tests.c @@ -0,0 +1,102 @@ +/* Copyright (c) 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. + * + * Tests for firmware 2common.c + */ + +#include "2sysincludes.h" +#include "2common.h" + +#include "test_common.h" + +/** + * Test alignment functions + */ +static void test_align(void) +{ + uint64_t buf[4]; + uint8_t *p0, *ptr; + uint32_t size; + + /* Already aligned */ + p0 = (uint8_t *)buf; + ptr = p0; + size = 16; + TEST_EQ(vb2_align(&ptr, &size, 4, 16), 0, "vb2_align() aligned"); + TEST_EQ(vb2_offset_of(p0, ptr), 0, "ptr"); + TEST_EQ(size, 16, " size"); + TEST_NEQ(vb2_align(&ptr, &size, 4, 17), 0, "vb2_align() small"); + + /* Offset */ + ptr = p0 + 1; + size = 15; + TEST_EQ(vb2_align(&ptr, &size, 4, 12), 0, "vb2_align() offset"); + TEST_EQ(vb2_offset_of(p0, ptr), 4, "ptr"); + TEST_EQ(size, 12, " size"); + + /* Offset, now too small */ + ptr = p0 + 1; + size = 15; + TEST_NEQ(vb2_align(&ptr, &size, 4, 15), 0, "vb2_align() offset small"); + + /* Offset, too small even to align */ + ptr = p0 + 1; + size = 1; + TEST_NEQ(vb2_align(&ptr, &size, 4, 1), 0, "vb2_align() offset tiny"); +} + +/** + * Test work buffer functions + */ +static void test_workbuf(void) +{ + uint64_t buf[8]; + uint8_t *p0 = (uint8_t *)buf, *ptr; + struct vb2_workbuf wb; + + /* Init */ + vb2_workbuf_init(&wb, p0, 32); + TEST_EQ(vb2_offset_of(p0, wb.buf), 0, "Workbuf init aligned"); + TEST_EQ(wb.size, 32, " size"); + + vb2_workbuf_init(&wb, p0 + 4, 32); + TEST_EQ(vb2_offset_of(p0, wb.buf), 8, "Workbuf init unaligned"); + TEST_EQ(wb.size, 28, " size"); + + vb2_workbuf_init(&wb, p0 + 2, 5); + TEST_EQ(wb.size, 0, "Workbuf init tiny unaligned size"); + + /* Alloc rounds up */ + vb2_workbuf_init(&wb, p0, 32); + ptr = vb2_workbuf_alloc(&wb, 22); + TEST_EQ(vb2_offset_of(p0, ptr), 0, "Workbuf alloc"); + TEST_EQ(vb2_offset_of(p0, wb.buf), 24, " buf"); + TEST_EQ(wb.size, 8, " size"); + + vb2_workbuf_init(&wb, p0, 32); + TEST_PTR_EQ(vb2_workbuf_alloc(&wb, 33), NULL, "Workbuf alloc too big"); + + /* Free reverses alloc */ + vb2_workbuf_init(&wb, p0, 32); + vb2_workbuf_alloc(&wb, 22); + vb2_workbuf_free(&wb, 22); + TEST_EQ(vb2_offset_of(p0, wb.buf), 0, "Workbuf free buf"); + TEST_EQ(wb.size, 32, " size"); + + /* Realloc keeps same pointer as alloc */ + vb2_workbuf_init(&wb, p0, 32); + vb2_workbuf_alloc(&wb, 6); + ptr = vb2_workbuf_realloc(&wb, 6, 21); + TEST_EQ(vb2_offset_of(p0, ptr), 0, "Workbuf realloc"); + TEST_EQ(vb2_offset_of(p0, wb.buf), 24, " buf"); + TEST_EQ(wb.size, 8, " size"); +} + +int main(int argc, char* argv[]) +{ + test_align(); + test_workbuf(); + + return gTestSuccess ? 0 : 255; +} |