diff options
author | Bill Richardson <wfrichar@chromium.org> | 2013-01-25 16:02:32 -0800 |
---|---|---|
committer | Bill Richardson <wfrichar@chromium.org> | 2013-01-25 16:03:25 -0800 |
commit | 9c45cd6562f27e6fbea792b0a3c7d000baa13bd4 (patch) | |
tree | 54d8fd79761e3e941a07cf6597c54af2ee7e73fd | |
parent | 5c05e01142e8de8d5c9d0582360f67f091b3cdb1 (diff) | |
parent | 786a5dca74387de5ffdc51ca5a099a4e90f406da (diff) | |
download | vboot-9c45cd6562f27e6fbea792b0a3c7d000baa13bd4.tar.gz |
Add logging capability, sync with m/master
BUG=none
BRANCH=none
TEST=none
Change-Id: If2438c007c2233501945db67d238fc5c7defef4b
24 files changed, 3985 insertions, 3936 deletions
@@ -36,8 +36,8 @@ # changed or appended. They must be defined before being used anywhere. # we should only run pwd once, not every time we refer to ${BUILD}. -_whereami := $(shell pwd) -BUILD ?= $(_whereami)/build +SRCDIR := $(shell pwd) +BUILD ?= $(SRCDIR)/build export BUILD # Target for 'make install' @@ -52,6 +52,29 @@ ifeq (${V},) Q := @ endif +# Architecture detection +_machname := $(shell uname -m) +HOST_ARCH ?= ${_machname} + +# ARCH and/or FIRMWARE_ARCH are defined by the Chromium OS ebuild. +# Pick a sane target architecture if none is defined. +ifeq (${ARCH},) + ARCH := ${HOST_ARCH} +else ifeq (${ARCH},i386) + override ARCH := x86 +else ifeq (${ARCH},amd64) + override ARCH := x86_64 +endif + +# FIRMWARE_ARCH is only defined by the Chromium OS ebuild if compiling +# for a firmware target (such as u-boot or depthcharge). It must map +# to the same consistent set of architectures as the host. +ifeq (${FIRMWARE_ARCH},i386) + override FIRMWARE_ARCH := x86 +else ifeq (${FIRMWARE_ARCH},amd64) + override FIRMWARE_ARCH := x86_64 +endif + # Provide default CC and CFLAGS for firmware builds; if you have any -D flags, # please add them after this point (e.g., -DVBOOT_DEBUG). # @@ -75,7 +98,7 @@ CFLAGS ?= -march=armv5 \ -fno-common -ffixed-r8 \ -mfloat-abi=hard -marm -mabi=aapcs-linux -mno-thumb-interwork \ ${COMMON_FLAGS} -else ifeq (${FIRMWARE_ARCH}, i386) +else ifeq (${FIRMWARE_ARCH}, x86) CC ?= i686-pc-linux-gnu-gcc # Drop -march=i386 to permit use of SSE instructions CFLAGS ?= \ @@ -103,11 +126,12 @@ endif # Create / use dependency files CFLAGS += -MMD -MF $@.d -# Code coverage. Run like this: COV=1 make runtests coverage +# Code coverage ifneq (${COV},) -COV_FLAGS = -O0 --coverage -CFLAGS += ${COV_FLAGS} -LDFLAGS += ${COV_FLAGS} + COV_FLAGS = -O0 --coverage + CFLAGS += ${COV_FLAGS} + LDFLAGS += ${COV_FLAGS} + COV_INFO = ${BUILD}/coverage.info endif # And a few more default utilities @@ -115,32 +139,13 @@ LD = ${CC} CXX ?= g++ # HEY: really? PKG_CONFIG ?= pkg-config -# Architecture detection. If we're cross-compiling, we may need to run unit -# tests inside QEMU. -_machname := $(shell uname -m) -HOST_ARCH ?= ${_machname} - -# Note: ARCH is defined by the Chromium OS ebuild. Pick a sane target -# architecture if none is defined. -ifeq (${ARCH},) - ARCH := ${HOST_ARCH} - ifeq (${ARCH}, x86_64) - ARCH := amd64 - endif -endif - # Determine QEMU architecture needed, if any ifeq (${ARCH},${HOST_ARCH}) # Same architecture; no need for QEMU QEMU_ARCH := -else ifeq (${HOST_ARCH}-${ARCH},x86_64-i386) +else ifeq (${HOST_ARCH}-${ARCH},x86_64-x86) # 64-bit host can run 32-bit targets directly QEMU_ARCH := -else ifeq (${HOST_ARCH}-${ARCH},x86_64-amd64) - # 64-bit host can run 64-bit directly - QEMU_ARCH := -else ifeq (${ARCH},amd64) - QEMU_ARCH := x86_64 else QEMU_ARCH := ${ARCH} endif @@ -152,21 +157,21 @@ endif ifeq (${QEMU_ARCH},) # Path to build output for running tests is same as for building BUILD_RUN = ${BUILD} + SRC_RUN = ${SRCDIR} else $(info Using qemu for testing.) # Path to build output for running tests is different in the chroot BUILD_RUN = $(subst ${SYSROOT},,${BUILD}) + SRC_RUN = $(subst ${SYSROOT},,${SRCDIR}) QEMU_BIN = qemu-${QEMU_ARCH} - QEMU_OPTS = -drop-ld-preload \ - -E LD_LIBRARY_PATH=/lib64:/lib:/usr/lib64:/usr/lib \ - -E HOME=${HOME} \ - -E BUILD=${BUILD_RUN} - QEMU_CMD = sudo chroot ${SYSROOT} ${BUILD_RUN}/${QEMU_BIN} ${QEMU_OPTS} -- - RUNTEST = ${QEMU_CMD} -endif + QEMU_RUN = ${BUILD_RUN}/${QEMU_BIN} + export QEMU_RUN + RUNTEST = tests/test_using_qemu.sh +endif +export BUILD_RUN # Some things only compile inside the Chromium OS chroot. # TODO: Those things should be in their own repo, not part of vboot_reference @@ -342,7 +347,6 @@ UTIL_NAMES = ${UTIL_NAMES_STATIC} \ signature_digest_utility \ tpm_init_temp_fix \ tpmc \ - vbutil_ec \ vbutil_firmware \ vbutil_kernel \ vbutil_key \ @@ -401,6 +405,7 @@ ALL_OBJS += ${TESTLIB_OBJS} TEST_NAMES = \ cgptlib_test \ rollback_index2_tests \ + rollback_index3_tests \ rsa_padding_test \ rsa_utility_tests \ rsa_verify_benchmark \ @@ -410,7 +415,6 @@ TEST_NAMES = \ tpm_bootmode_tests \ utility_string_tests \ utility_tests \ - vboot_nvstorage_test \ vboot_api_init_tests \ vboot_api_devmode_tests \ vboot_api_firmware_tests \ @@ -419,8 +423,9 @@ TEST_NAMES = \ vboot_common_tests \ vboot_common2_tests \ vboot_common3_tests \ - vboot_ec_tests \ - vboot_firmware_tests + vboot_display_tests \ + vboot_firmware_tests \ + vboot_nvstorage_test # Grrr ifneq (${IN_CHROOT},) @@ -466,6 +471,8 @@ TEST_NAMES += ${TLCL_TEST_NAMES} TEST_BINS = $(addprefix ${BUILD}/tests/,${TEST_NAMES}) ALL_DEPS += $(addsuffix .d,${TEST_BINS}) +# Directory containing test keys +TEST_KEYS = ${SRC_RUN}/tests/testkeys # ---------------------------------------------------------------------------- # TODO: why not make this include *all* the cgpt files, and simply have @@ -504,7 +511,7 @@ _dir_create := $(foreach d, \ # Default target. .PHONY: all -all: fwlib $(if ${FIRMWARE_ARCH},,host_stuff) +all: fwlib $(if ${FIRMWARE_ARCH},,host_stuff) $(if ${COV},coverage) # Host targets .PHONY: host_stuff @@ -521,17 +528,6 @@ clean: .PHONY: install install: cgpt_install utils_install futil_install -# Coverage -# TODO: only if COV=1 -# HEY - depend on runtests? -COV_INFO = ${BUILD}/coverage.info -.PHONY: coverage -coverage: - rm -f ${COV_INFO}* - lcov --capture --directory . --base-directory . -o ${COV_INFO}.1 - lcov --remove ${COV_INFO}.1 '/usr/*' -o ${COV_INFO} - genhtml ${COV_INFO} --output-directory ${BUILD}/coverage - # Don't delete intermediate object files .SECONDARY: @@ -551,7 +547,7 @@ ALL_DEPS += ${ALL_OBJS:%.o=%.o.d} # TPM_BLOCKING_CONTINUESELFTEST is defined if TPM_ContinueSelfTest blocks until # the self test has completed. -${FWLIB}: CFLAGS += -DTPM_BLOCKING_CONTINUESELFTEST +${FWLIB_OBJS}: CFLAGS += -DTPM_BLOCKING_CONTINUESELFTEST # TPM_MANUAL_SELFTEST is defined if the self test must be started manually # (with a call to TPM_ContinueSelfTest) instead of starting automatically at @@ -564,24 +560,24 @@ ${FWLIB}: CFLAGS += -DTPM_BLOCKING_CONTINUESELFTEST ifeq (${FIRMWARE_ARCH},i386) # Unrolling loops in cryptolib makes it faster -${FWLIB}: CFLAGS += -DUNROLL_LOOPS +${FWLIB_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 # Way to do things. See chrome-os-partner:7689, and the commit message # that made this change. -${FWLIB}: CFLAGS += -DSAVE_LOCALE_IMMEDIATELY +${FWLIB_OBJS}: CFLAGS += -DSAVE_LOCALE_IMMEDIATELY # On x86 we don't actually read the GBB data into RAM until it is needed. # Therefore it makes sense to cache it rather than reading it each time. # Enable this feature. -${FWLIB}: CFLAGS += -DCOPY_BMP_DATA +${FWLIB_OBJS}: CFLAGS += -DCOPY_BMP_DATA endif ifeq (${FIRMWARE_ARCH},) # Disable rollback TPM when compiling locally, since otherwise # load_kernel_test attempts to talk to the TPM. -${FWLIB}: CFLAGS += -DDISABLE_ROLLBACK_TPM +${FWLIB_OBJS}: CFLAGS += -DDISABLE_ROLLBACK_TPM endif .PHONY: fwlib @@ -804,12 +800,15 @@ ${BUILD}/utility/dump_kernel_config: ${DUMPKERNELCONFIGLIB} # GBB utility needs C++ linker. TODO: It shouldn't. ${BUILD}/utility/gbb_utility: LD = ${CXX} +# Because we play some clever linker script games to add new commands without +# changing any header files, futility must be linked with ld.bfd, not gold. +${FUTIL_BIN}: LDFLAGS += -fuse-ld=bfd + # Some utilities need external crypto functions ${BUILD}/utility/dumpRSAPublicKey: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/utility/pad_digest_utility: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/utility/signature_digest_utility: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/utility/dev_sign_file: LDLIBS += ${CRYPTO_LIBS} -${BUILD}/utility/vbutil_ec: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/utility/vbutil_firmware: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/utility/vbutil_kernel: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/utility/vbutil_key: LDLIBS += ${CRYPTO_LIBS} @@ -818,8 +817,6 @@ ${BUILD}/utility/vbutil_keyblock: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/host/linktest/main: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/tests/vboot_common2_tests: LDLIBS += ${CRYPTO_LIBS} ${BUILD}/tests/vboot_common3_tests: LDLIBS += ${CRYPTO_LIBS} -${BUILD}/tests/vboot_ec_tests: LDLIBS += ${CRYPTO_LIBS} - ${BUILD}/utility/bmpblk_utility: LD = ${CXX} ${BUILD}/utility/bmpblk_utility: LDLIBS = -llzma -lyaml @@ -931,13 +928,13 @@ runtestscripts: test_setup genfuzztestcases tests/run_cgpt_tests.sh ${BUILD_RUN}/cgpt/cgpt tests/run_preamble_tests.sh tests/run_rsa_tests.sh - tests/run_vboot_common_tests.sh tests/run_vbutil_kernel_arg_tests.sh tests/run_vbutil_tests.sh .PHONY: runmisctests runmisctests: test_setup ${RUNTEST} ${BUILD_RUN}/tests/rollback_index2_tests + ${RUNTEST} ${BUILD_RUN}/tests/rollback_index3_tests ${RUNTEST} ${BUILD_RUN}/tests/rsa_utility_tests ${RUNTEST} ${BUILD_RUN}/tests/sha_tests ${RUNTEST} ${BUILD_RUN}/tests/stateful_util_tests @@ -948,7 +945,12 @@ runmisctests: test_setup ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_init_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_firmware_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_audio_tests + ${RUNTEST} ${BUILD_RUN}/tests/vboot_common_tests + ${RUNTEST} ${BUILD_RUN}/tests/vboot_common2_tests ${TEST_KEYS} + ${RUNTEST} ${BUILD_RUN}/tests/vboot_common3_tests ${TEST_KEYS} + ${RUNTEST} ${BUILD_RUN}/tests/vboot_display_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_firmware_tests + ${RUNTEST} ${BUILD_RUN}/tests/vboot_nvstorage_test .PHONY: runfutiltests runfutiltests: DESTDIR := ${TEST_INSTALL_DIR} @@ -957,13 +959,13 @@ runfutiltests: test_setup install futility/tests/run_futility_tests.sh ${DESTDIR} # Run long tests, including all permutations of encryption keys (instead of -# just the ones we use) and tests of currently-unused code (e.g. vboot_ec). +# just the ones we use) and tests of currently-unused code. # Not run by automated build. .PHONY: runlongtests runlongtests: test_setup genkeys genfuzztestcases + ${RUNTEST} ${BUILD_RUN}/tests/vboot_common2_tests ${TEST_KEYS} --all + ${RUNTEST} ${BUILD_RUN}/tests/vboot_common3_tests ${TEST_KEYS} --all tests/run_preamble_tests.sh --all - tests/run_vboot_common_tests.sh --all - tests/run_vboot_ec_tests.sh tests/run_vbutil_tests.sh --all # TODO: tests to run when ported to new API @@ -975,3 +977,29 @@ runlongtests: test_setup genkeys genfuzztestcases # ${BUILD}/tests/firmware_rollback_tests # ${BUILD}/tests/kernel_rollback_tests +# Code coverage +.PHONY: coverage_init +coverage_init: test_setup + rm -f ${COV_INFO}* + lcov -c -i -d . -b . -o ${COV_INFO}.initial + +.PHONY: coverage_html +coverage_html: + lcov -c -d . -b . -o ${COV_INFO}.tests + lcov -a ${COV_INFO}.initial -a ${COV_INFO}.tests -o ${COV_INFO}.total + lcov -r ${COV_INFO}.total '/usr/*' '*/linktest/*' -o ${COV_INFO}.local + genhtml ${COV_INFO}.local -o ${BUILD}/coverage + +# Generate addtional coverage stats just for firmware subdir, because the +# per-directory stats for the whole project don't include their own subdirs. + lcov -e ${COV_INFO}.local '${SRCDIR}/firmware/*' \ + -o ${COV_INFO}.firmware + +.PHONY: coverage +ifeq (${COV},) +coverage: + $(error Build coverage like this: make clean && COV=1 make) +else +coverage: coverage_init runtests coverage_html +endif + diff --git a/firmware/arch/i386/include/biosincludes.h b/firmware/arch/x86/include/biosincludes.h index 5f6b1cc2..5f6b1cc2 100644 --- a/firmware/arch/i386/include/biosincludes.h +++ b/firmware/arch/x86/include/biosincludes.h diff --git a/firmware/lib/cgptlib/include/cgptlib.h b/firmware/lib/cgptlib/include/cgptlib.h index b6b27c96..6633733e 100644 --- a/firmware/lib/cgptlib/include/cgptlib.h +++ b/firmware/lib/cgptlib/include/cgptlib.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. */ @@ -21,6 +21,8 @@ enum { GPT_ERROR_START_LBA_OVERLAP, GPT_ERROR_END_LBA_OVERLAP, GPT_ERROR_DUP_GUID, + /* Number of errors */ + GPT_ERROR_COUNT }; /* Bit masks for GptData.modified field. */ diff --git a/firmware/lib/cgptlib/include/cgptlib_internal.h b/firmware/lib/cgptlib/include/cgptlib_internal.h index f899eab0..36e598c2 100644 --- a/firmware/lib/cgptlib/include/cgptlib_internal.h +++ b/firmware/lib/cgptlib/include/cgptlib_internal.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2011 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. */ @@ -89,6 +89,15 @@ uint32_t HeaderCrc(GptHeader* h); * Returns 0 if entries are valid, 1 if invalid. */ int CheckEntries(GptEntry* entries, GptHeader* h); +/* Return 0 if the GptHeaders are the same for all fields which don't + * differ between the primary and secondary headers - that is, all + * fields other than: + * + * my_lba + * alternate_lba + * entries_lba */ +int HeaderFieldsSame(GptHeader *h1, GptHeader *h2); + /* Check GptData, headers, entries. * * If successful, sets gpt->valid_headers and gpt->valid_entries and returns diff --git a/firmware/lib/include/vboot_common.h b/firmware/lib/include/vboot_common.h index 76706bf6..fe886ce4 100644 --- a/firmware/lib/include/vboot_common.h +++ b/firmware/lib/include/vboot_common.h @@ -87,11 +87,6 @@ int VerifyData(const uint8_t* data, uint64_t size, const VbSignature* sig, int VerifyDigest(const uint8_t* digest, const VbSignature *sig, const RSAPublicKey* key); -/* Uses [key] algorithm to hash [data], then compares that to the expected - * [hash]. Returns 0 if they're equal, non-zero if error. */ -int EqualData(const uint8_t* data, uint64_t size, const VbSignature *hash, - const RSAPublicKey* key); - /* Checks the sanity of a key block of size [size] bytes, using public * key [key]. If hash_only is non-zero, uses only the block checksum * to verify the key block. Header fields are also checked for @@ -100,14 +95,6 @@ int KeyBlockVerify(const VbKeyBlockHeader* block, uint64_t size, const VbPublicKey *key, int hash_only); -/* Checks the sanity of an EC preamble of size [size] bytes, - * using public key [key]. - * - * Returns VBOOT_SUCCESS if successful. */ -int VerifyECPreamble(const VbECPreambleHeader* preamble, - uint64_t size, const RSAPublicKey* key); - - /* Checks the sanity of a firmware preamble of size [size] bytes, * using public key [key]. * diff --git a/firmware/lib/include/vboot_display.h b/firmware/lib/include/vboot_display.h index 57688aed..b9ec04c0 100644 --- a/firmware/lib/include/vboot_display.h +++ b/firmware/lib/include/vboot_display.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. * @@ -19,5 +19,8 @@ VbError_t VbDisplayDebugInfo(VbCommonParams* cparams, VbNvContext *vncptr); VbError_t VbCheckDisplayKey(VbCommonParams* cparams, uint32_t key, VbNvContext *vncptr); +/* Internal functions, for unit testing */ +const char *RecoveryReasonString(uint8_t code); + #endif /* VBOOT_REFERENCE_VBOOT_DISPLAY_H_ */ diff --git a/firmware/lib/rollback_index.c b/firmware/lib/rollback_index.c index ee89d7e6..82323235 100644 --- a/firmware/lib/rollback_index.c +++ b/firmware/lib/rollback_index.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. * @@ -39,246 +39,281 @@ __pragma(warning (disable: 4127)) } while (0) -uint32_t TPMClearAndReenable(void) { - VBDEBUG(("TPM: Clear and re-enable\n")); - RETURN_ON_FAILURE(TlclForceClear()); - RETURN_ON_FAILURE(TlclSetEnable()); - RETURN_ON_FAILURE(TlclSetDeactivated(0)); +uint32_t TPMClearAndReenable(void) +{ + VBDEBUG(("TPM: Clear and re-enable\n")); + RETURN_ON_FAILURE(TlclForceClear()); + RETURN_ON_FAILURE(TlclSetEnable()); + RETURN_ON_FAILURE(TlclSetDeactivated(0)); - return TPM_SUCCESS; + return TPM_SUCCESS; } - -uint32_t SafeWrite(uint32_t index, const void* data, uint32_t length) { - uint32_t result = TlclWrite(index, data, length); - if (result == TPM_E_MAXNVWRITES) { - RETURN_ON_FAILURE(TPMClearAndReenable()); - return TlclWrite(index, data, length); - } else { - return result; - } +uint32_t SafeWrite(uint32_t index, const void *data, uint32_t length) +{ + uint32_t result = TlclWrite(index, data, length); + if (result == TPM_E_MAXNVWRITES) { + RETURN_ON_FAILURE(TPMClearAndReenable()); + return TlclWrite(index, data, length); + } else { + return result; + } } - -uint32_t SafeDefineSpace(uint32_t index, uint32_t perm, uint32_t size) { - uint32_t result = TlclDefineSpace(index, perm, size); - if (result == TPM_E_MAXNVWRITES) { - RETURN_ON_FAILURE(TPMClearAndReenable()); - return TlclDefineSpace(index, perm, size); - } else { - return result; - } +uint32_t SafeDefineSpace(uint32_t index, uint32_t perm, uint32_t size) +{ + uint32_t result = TlclDefineSpace(index, perm, size); + if (result == TPM_E_MAXNVWRITES) { + RETURN_ON_FAILURE(TPMClearAndReenable()); + return TlclDefineSpace(index, perm, size); + } else { + return result; + } } - /* Functions to read and write firmware and kernel spaces. */ -uint32_t ReadSpaceFirmware(RollbackSpaceFirmware* rsf) { - uint32_t r; - int attempts = 3; - - while (attempts--) { - r = TlclRead(FIRMWARE_NV_INDEX, rsf, sizeof(RollbackSpaceFirmware)); - if (r != TPM_SUCCESS) - return r; - - /* No CRC in this version, so we'll create one when we write it. Note that - * we're marking this as version 2, not ROLLBACK_SPACE_FIRMWARE_VERSION, - * because version 2 just added the CRC. Later versions will need to - * set default values for any extra fields explicitly (probably here). */ - if (rsf->struct_version < 2) { - rsf->struct_version = 2; /* Danger Will Robinson! Danger! */ - return TPM_SUCCESS; - } - - /* If the CRC is good, we're done. If it's bad, try a couple more times to - * see if it gets better before we give up. It could just be noise. */ - if (rsf->crc8 == Crc8(rsf, offsetof(RollbackSpaceFirmware, crc8))) - return TPM_SUCCESS; - - VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); - } - - VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); - return TPM_E_CORRUPTED_STATE; +uint32_t ReadSpaceFirmware(RollbackSpaceFirmware *rsf) +{ + uint32_t r; + int attempts = 3; + + while (attempts--) { + r = TlclRead(FIRMWARE_NV_INDEX, rsf, + sizeof(RollbackSpaceFirmware)); + if (r != TPM_SUCCESS) + return r; + + /* + * No CRC in this version, so we'll create one when we write + * it. Note that we're marking this as version 2, not + * ROLLBACK_SPACE_FIRMWARE_VERSION, because version 2 just + * added the CRC. Later versions will need to set default + * values for any extra fields explicitly (probably here). + */ + if (rsf->struct_version < 2) { + /* Danger Will Robinson! Danger! */ + rsf->struct_version = 2; + return TPM_SUCCESS; + } + + /* + * If the CRC is good, we're done. If it's bad, try a couple + * more times to see if it gets better before we give up. It + * could just be noise. + */ + if (rsf->crc8 == Crc8(rsf, + offsetof(RollbackSpaceFirmware, crc8))) + return TPM_SUCCESS; + + VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); + } + + VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); + return TPM_E_CORRUPTED_STATE; } -uint32_t WriteSpaceFirmware(RollbackSpaceFirmware* rsf) { - RollbackSpaceFirmware rsf2; - uint32_t r; - int attempts = 3; - - /* All writes should use struct_version 2 or greater. */ - if (rsf->struct_version < 2) - rsf->struct_version = 2; - rsf->crc8 = Crc8(rsf, offsetof(RollbackSpaceFirmware, crc8)); - - while (attempts--) { - r = SafeWrite(FIRMWARE_NV_INDEX, rsf, sizeof(RollbackSpaceFirmware)); - /* Can't write, not gonna try again */ - if (r != TPM_SUCCESS) - return r; - - /* Read it back to be sure it got the right values. */ - r = ReadSpaceFirmware(&rsf2); /* This checks the CRC */ - if (r == TPM_SUCCESS) - return r; - - VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); - /* Try writing it again. Maybe it was garbled on the way out. */ - } - - VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); - return TPM_E_CORRUPTED_STATE; +uint32_t WriteSpaceFirmware(RollbackSpaceFirmware *rsf) +{ + RollbackSpaceFirmware rsf2; + uint32_t r; + int attempts = 3; + + /* All writes should use struct_version 2 or greater. */ + if (rsf->struct_version < 2) + rsf->struct_version = 2; + rsf->crc8 = Crc8(rsf, offsetof(RollbackSpaceFirmware, crc8)); + + while (attempts--) { + r = SafeWrite(FIRMWARE_NV_INDEX, rsf, + sizeof(RollbackSpaceFirmware)); + /* Can't write, not gonna try again */ + if (r != TPM_SUCCESS) + return r; + + /* Read it back to be sure it got the right values. */ + r = ReadSpaceFirmware(&rsf2); /* This checks the CRC */ + if (r == TPM_SUCCESS) + return r; + + VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); + /* Try writing it again. Maybe it was garbled on the way out. */ + } + + VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); + return TPM_E_CORRUPTED_STATE; } -uint32_t SetVirtualDevMode(int val) { - RollbackSpaceFirmware rsf; - - VBDEBUG(("TPM: Entering %s()\n", __func__)); - if (TPM_SUCCESS != ReadSpaceFirmware(&rsf)) - return VBERROR_TPM_FIRMWARE_SETUP; - - VBDEBUG(("TPM: flags were 0x%02x\n", rsf.flags)); - if (val) - rsf.flags |= FLAG_VIRTUAL_DEV_MODE_ON; - else - rsf.flags &= ~FLAG_VIRTUAL_DEV_MODE_ON; - /* NOTE: This doesn't update the FLAG_LAST_BOOT_DEVELOPER bit */ - VBDEBUG(("TPM: flags are now 0x%02x\n", rsf.flags)); - - if (TPM_SUCCESS != WriteSpaceFirmware(&rsf)) - return VBERROR_TPM_SET_BOOT_MODE_STATE; - - VBDEBUG(("TPM: Leaving %s()\n", __func__)); - return VBERROR_SUCCESS; +uint32_t SetVirtualDevMode(int val) +{ + RollbackSpaceFirmware rsf; + + VBDEBUG(("TPM: Entering %s()\n", __func__)); + if (TPM_SUCCESS != ReadSpaceFirmware(&rsf)) + return VBERROR_TPM_FIRMWARE_SETUP; + + VBDEBUG(("TPM: flags were 0x%02x\n", rsf.flags)); + if (val) + rsf.flags |= FLAG_VIRTUAL_DEV_MODE_ON; + else + rsf.flags &= ~FLAG_VIRTUAL_DEV_MODE_ON; + /* + * NOTE: This doesn't update the FLAG_LAST_BOOT_DEVELOPER bit. That + * will be done by SetupTPM() on the next boot. + */ + VBDEBUG(("TPM: flags are now 0x%02x\n", rsf.flags)); + + if (TPM_SUCCESS != WriteSpaceFirmware(&rsf)) + return VBERROR_TPM_SET_BOOT_MODE_STATE; + + VBDEBUG(("TPM: Leaving %s()\n", __func__)); + return VBERROR_SUCCESS; } -uint32_t ReadSpaceKernel(RollbackSpaceKernel* rsk) { - uint32_t r; - int attempts = 3; - - while (attempts--) { - r = TlclRead(KERNEL_NV_INDEX, rsk, sizeof(RollbackSpaceKernel)); - if (r != TPM_SUCCESS) - return r; - - /* No CRC in this version, so we'll create one when we write it. Note that - * we're marking this as version 2, not ROLLBACK_SPACE_KERNEL_VERSION, - * because version 2 just added the CRC. Later versions will need to - * set default values for any extra fields explicitly (probably here). */ - if (rsk->struct_version < 2) { - rsk->struct_version = 2; /* Danger Will Robinson! Danger! */ - return TPM_SUCCESS; - } - - /* If the CRC is good, we're done. If it's bad, try a couple more times to - * see if it gets better before we give up. It could just be noise. */ - if (rsk->crc8 == Crc8(rsk, offsetof(RollbackSpaceKernel, crc8))) - return TPM_SUCCESS; - - VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); - } - - VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); - return TPM_E_CORRUPTED_STATE; +uint32_t ReadSpaceKernel(RollbackSpaceKernel *rsk) +{ + uint32_t r; + int attempts = 3; + + while (attempts--) { + r = TlclRead(KERNEL_NV_INDEX, rsk, sizeof(RollbackSpaceKernel)); + if (r != TPM_SUCCESS) + return r; + + /* + * No CRC in this version, so we'll create one when we write + * it. Note that we're marking this as version 2, not + * ROLLBACK_SPACE_KERNEL_VERSION, because version 2 just added + * the CRC. Later versions will need to set default values for + * any extra fields explicitly (probably here). + */ + if (rsk->struct_version < 2) { + /* Danger Will Robinson! Danger! */ + rsk->struct_version = 2; + return TPM_SUCCESS; + } + + /* + * If the CRC is good, we're done. If it's bad, try a couple + * more times to see if it gets better before we give up. It + * could just be noise. + */ + if (rsk->crc8 == Crc8(rsk, offsetof(RollbackSpaceKernel, crc8))) + return TPM_SUCCESS; + + VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); + } + + VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); + return TPM_E_CORRUPTED_STATE; } -uint32_t WriteSpaceKernel(RollbackSpaceKernel* rsk) { - RollbackSpaceKernel rsk2; - uint32_t r; - int attempts = 3; - - /* All writes should use struct_version 2 or greater. */ - if (rsk->struct_version < 2) - rsk->struct_version = 2; - rsk->crc8 = Crc8(rsk, offsetof(RollbackSpaceKernel, crc8)); - - while (attempts--) { - r = SafeWrite(KERNEL_NV_INDEX, rsk, sizeof(RollbackSpaceKernel)); - /* Can't write, not gonna try again */ - if (r != TPM_SUCCESS) - return r; - - /* Read it back to be sure it got the right values. */ - r = ReadSpaceKernel(&rsk2); /* This checks the CRC */ - if (r == TPM_SUCCESS) - return r; - - VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); - /* Try writing it again. Maybe it was garbled on the way out. */ - } - - VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); - return TPM_E_CORRUPTED_STATE; +uint32_t WriteSpaceKernel(RollbackSpaceKernel *rsk) +{ + RollbackSpaceKernel rsk2; + uint32_t r; + int attempts = 3; + + /* All writes should use struct_version 2 or greater. */ + if (rsk->struct_version < 2) + rsk->struct_version = 2; + rsk->crc8 = Crc8(rsk, offsetof(RollbackSpaceKernel, crc8)); + + while (attempts--) { + r = SafeWrite(KERNEL_NV_INDEX, rsk, + sizeof(RollbackSpaceKernel)); + /* Can't write, not gonna try again */ + if (r != TPM_SUCCESS) + return r; + + /* Read it back to be sure it got the right values. */ + r = ReadSpaceKernel(&rsk2); /* This checks the CRC */ + if (r == TPM_SUCCESS) + return r; + + VBDEBUG(("TPM: %s() - bad CRC\n", __func__)); + /* Try writing it again. Maybe it was garbled on the way out. */ + } + + VBDEBUG(("TPM: %s() - too many bad CRCs, giving up\n", __func__)); + return TPM_E_CORRUPTED_STATE; } - -uint32_t OneTimeInitializeTPM(RollbackSpaceFirmware* rsf, - RollbackSpaceKernel* rsk) { - static const RollbackSpaceFirmware rsf_init = { - .struct_version = ROLLBACK_SPACE_FIRMWARE_VERSION, - }; - static const RollbackSpaceKernel rsk_init = { - .struct_version = ROLLBACK_SPACE_KERNEL_VERSION, - .uid = ROLLBACK_SPACE_KERNEL_UID, - }; - TPM_PERMANENT_FLAGS pflags; - uint32_t result; - - VBDEBUG(("TPM: One-time initialization\n")); - - /* Do a full test. This only happens the first time the device is turned on - * in the factory, so performance is not an issue. This is almost certainly - * not necessary, but it gives us more confidence about some code paths below - * that are difficult to test---specifically the ones that set lifetime - * flags, and are only executed once per physical TPM. */ - result = TlclSelfTestFull(); - if (result != TPM_SUCCESS) - return result; - - result = TlclGetPermanentFlags(&pflags); - if (result != TPM_SUCCESS) - return result; - - /* TPM may come from the factory without physical presence finalized. Fix - * if necessary. */ - VBDEBUG(("TPM: physicalPresenceLifetimeLock=%d\n", - pflags.physicalPresenceLifetimeLock)); - if (!pflags.physicalPresenceLifetimeLock) { - VBDEBUG(("TPM: Finalizing physical presence\n")); - RETURN_ON_FAILURE(TlclFinalizePhysicalPresence()); - } - - /* The TPM will not enforce the NV authorization restrictions until the - * execution of a TPM_NV_DefineSpace with the handle of TPM_NV_INDEX_LOCK. - * Here we create that space if it doesn't already exist. */ - VBDEBUG(("TPM: nvLocked=%d\n", pflags.nvLocked)); - if (!pflags.nvLocked) { - VBDEBUG(("TPM: Enabling NV locking\n")); - RETURN_ON_FAILURE(TlclSetNvLocked()); - } - - /* Clear TPM owner, in case the TPM is already owned for some reason. */ - VBDEBUG(("TPM: Clearing owner\n")); - RETURN_ON_FAILURE(TPMClearAndReenable()); - - /* Initializes the firmware and kernel spaces */ - Memcpy(rsf, &rsf_init, sizeof(RollbackSpaceFirmware)); - Memcpy(rsk, &rsk_init, sizeof(RollbackSpaceKernel)); - - /* Defines and sets firmware and kernel spaces */ - RETURN_ON_FAILURE(SafeDefineSpace(KERNEL_NV_INDEX, TPM_NV_PER_PPWRITE, - sizeof(RollbackSpaceKernel))); - RETURN_ON_FAILURE(WriteSpaceKernel(rsk)); - RETURN_ON_FAILURE(SafeDefineSpace(FIRMWARE_NV_INDEX, - TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE, - sizeof(RollbackSpaceFirmware))); - RETURN_ON_FAILURE(WriteSpaceFirmware(rsf)); - return TPM_SUCCESS; +uint32_t OneTimeInitializeTPM(RollbackSpaceFirmware *rsf, + RollbackSpaceKernel *rsk) +{ + static const RollbackSpaceFirmware rsf_init = { + .struct_version = ROLLBACK_SPACE_FIRMWARE_VERSION, + }; + static const RollbackSpaceKernel rsk_init = { + .struct_version = ROLLBACK_SPACE_KERNEL_VERSION, + .uid = ROLLBACK_SPACE_KERNEL_UID, + }; + TPM_PERMANENT_FLAGS pflags; + uint32_t result; + + VBDEBUG(("TPM: One-time initialization\n")); + + /* + * Do a full test. This only happens the first time the device is + * turned on in the factory, so performance is not an issue. This is + * almost certainly not necessary, but it gives us more confidence + * about some code paths below that are difficult to + * test---specifically the ones that set lifetime flags, and are only + * executed once per physical TPM. + */ + result = TlclSelfTestFull(); + if (result != TPM_SUCCESS) + return result; + + result = TlclGetPermanentFlags(&pflags); + if (result != TPM_SUCCESS) + return result; + + /* + * TPM may come from the factory without physical presence finalized. + * Fix if necessary. + */ + VBDEBUG(("TPM: physicalPresenceLifetimeLock=%d\n", + pflags.physicalPresenceLifetimeLock)); + if (!pflags.physicalPresenceLifetimeLock) { + VBDEBUG(("TPM: Finalizing physical presence\n")); + RETURN_ON_FAILURE(TlclFinalizePhysicalPresence()); + } + + /* + * The TPM will not enforce the NV authorization restrictions until the + * execution of a TPM_NV_DefineSpace with the handle of + * TPM_NV_INDEX_LOCK. Here we create that space if it doesn't already + * exist. */ + VBDEBUG(("TPM: nvLocked=%d\n", pflags.nvLocked)); + if (!pflags.nvLocked) { + VBDEBUG(("TPM: Enabling NV locking\n")); + RETURN_ON_FAILURE(TlclSetNvLocked()); + } + + /* Clear TPM owner, in case the TPM is already owned for some reason. */ + VBDEBUG(("TPM: Clearing owner\n")); + RETURN_ON_FAILURE(TPMClearAndReenable()); + + /* Initializes the firmware and kernel spaces */ + Memcpy(rsf, &rsf_init, sizeof(RollbackSpaceFirmware)); + Memcpy(rsk, &rsk_init, sizeof(RollbackSpaceKernel)); + + /* Defines and sets firmware and kernel spaces */ + RETURN_ON_FAILURE(SafeDefineSpace(KERNEL_NV_INDEX, TPM_NV_PER_PPWRITE, + sizeof(RollbackSpaceKernel))); + RETURN_ON_FAILURE(WriteSpaceKernel(rsk)); + RETURN_ON_FAILURE(SafeDefineSpace( + FIRMWARE_NV_INDEX, + TPM_NV_PER_GLOBALLOCK | TPM_NV_PER_PPWRITE, + sizeof(RollbackSpaceFirmware))); + RETURN_ON_FAILURE(WriteSpaceFirmware(rsf)); + return TPM_SUCCESS; } -/* SetupTPM starts the TPM and establishes the root of trust for the +/* + * SetupTPM starts the TPM and establishes the root of trust for the * anti-rollback mechanism. SetupTPM can fail for three reasons. 1 A bug. 2 a * TPM hardware failure. 3 An unexpected TPM state due to some attack. In * general we cannot easily distinguish the kind of failure, so our strategy is @@ -299,38 +334,40 @@ uint32_t OneTimeInitializeTPM(RollbackSpaceFirmware* rsf, */ uint32_t SetupTPM(int recovery_mode, int developer_mode, int disable_dev_request, int clear_tpm_owner_request, - RollbackSpaceFirmware* rsf) { - - uint8_t in_flags; - uint8_t disable; - uint8_t deactivated; - uint32_t result; + RollbackSpaceFirmware* rsf) +{ + uint8_t in_flags; + uint8_t disable; + uint8_t deactivated; + uint32_t result; - VBDEBUG(("TPM: SetupTPM(r%d, d%d)\n", recovery_mode, developer_mode)); + VBDEBUG(("TPM: SetupTPM(r%d, d%d)\n", recovery_mode, developer_mode)); - if (recovery_mode) - g_rollback_recovery_mode = 1; /* Global variables are usable in - * recovery mode */ + /* Global variables are usable in recovery mode */ + if (recovery_mode) + g_rollback_recovery_mode = 1; - RETURN_ON_FAILURE(TlclLibInit()); + RETURN_ON_FAILURE(TlclLibInit()); #ifdef TEGRA_SOFT_REBOOT_WORKAROUND - result = TlclStartup(); - if (result == TPM_E_INVALID_POSTINIT) { - /* Some prototype hardware doesn't reset the TPM on a CPU reset. We do a - * hard reset to get around this. - */ - VBDEBUG(("TPM: soft reset detected\n", result)); - return TPM_E_MUST_REBOOT; - } else if (result != TPM_SUCCESS) { - VBDEBUG(("TPM: TlclStartup returned %08x\n", result)); - return result; - } + result = TlclStartup(); + if (result == TPM_E_INVALID_POSTINIT) { + /* + * Some prototype hardware doesn't reset the TPM on a CPU + * reset. We do a hard reset to get around this. + */ + VBDEBUG(("TPM: soft reset detected\n", result)); + return TPM_E_MUST_REBOOT; + } else if (result != TPM_SUCCESS) { + VBDEBUG(("TPM: TlclStartup returned %08x\n", result)); + return result; + } #else - RETURN_ON_FAILURE(TlclStartup()); + RETURN_ON_FAILURE(TlclStartup()); #endif - /* Some TPMs start the self test automatically at power on. In that case we + /* + * Some TPMs start the self test automatically at power on. In that case we * don't need to call ContinueSelfTest. On some (other) TPMs, * ContinueSelfTest may block. In that case, we definitely don't want to * call it here. For TPMs in the intersection of these two sets, we're @@ -346,225 +383,249 @@ uint32_t SetupTPM(int recovery_mode, int developer_mode, #ifdef TPM_BLOCKING_CONTINUESELFTEST #warning "lousy TPM!" #endif - RETURN_ON_FAILURE(TlclContinueSelfTest()); + RETURN_ON_FAILURE(TlclContinueSelfTest()); #endif - result = TlclAssertPhysicalPresence(); - if (result != TPM_SUCCESS) { - /* It is possible that the TPM was delivered with the physical presence - * command disabled. This tries enabling it, then tries asserting PP - * again. - */ - RETURN_ON_FAILURE(TlclPhysicalPresenceCMDEnable()); - RETURN_ON_FAILURE(TlclAssertPhysicalPresence()); - } - - /* Checks that the TPM is enabled and activated. */ - RETURN_ON_FAILURE(TlclGetFlags(&disable, &deactivated, NULL)); - if (disable || deactivated) { - VBDEBUG(("TPM: disabled (%d) or deactivated (%d). Fixing...\n", - disable, deactivated)); - RETURN_ON_FAILURE(TlclSetEnable()); - RETURN_ON_FAILURE(TlclSetDeactivated(0)); - VBDEBUG(("TPM: Must reboot to re-enable\n")); - return TPM_E_MUST_REBOOT; - } - - /* Reads the firmware space. */ - result = ReadSpaceFirmware(rsf); - if (TPM_E_BADINDEX == result) { - RollbackSpaceKernel rsk; - - /* This is the first time we've run, and the TPM has not been - * initialized. This initializes it. */ - VBDEBUG(("TPM: Not initialized yet.\n")); - RETURN_ON_FAILURE(OneTimeInitializeTPM(rsf, &rsk)); - } else if (TPM_SUCCESS != result) { - VBDEBUG(("TPM: Firmware space in a bad state; giving up.\n")); - return TPM_E_CORRUPTED_STATE; - } - VBDEBUG(("TPM: Firmware space sv%d f%x v%x\n", - rsf->struct_version, rsf->flags, rsf->fw_versions)); - in_flags = rsf->flags; - - /* If we've been asked to clear the virtual dev-mode flag, do so now */ - if (disable_dev_request) { - rsf->flags &= ~FLAG_VIRTUAL_DEV_MODE_ON; - VBDEBUG(("TPM: Clearing virt dev-switch: f%x\n", rsf->flags)); - } - - /* The developer_mode value that's passed in is only set by a hardware - * dev-switch. We should OR it with the virtual switch, whether or not the - * virtual switch is used. If it's not used, it shouldn't change, so it - * doesn't matter. */ - if (rsf->flags & FLAG_VIRTUAL_DEV_MODE_ON) - developer_mode = 1; - - /* Clears ownership if developer flag has toggled, or if an owner-clear has - * been requested. */ - if ((developer_mode ? FLAG_LAST_BOOT_DEVELOPER : 0) != - (in_flags & FLAG_LAST_BOOT_DEVELOPER)) { - VBDEBUG(("TPM: Developer flag changed; clearing owner.\n")); - RETURN_ON_FAILURE(TPMClearAndReenable()); - } else if (clear_tpm_owner_request) { - VBDEBUG(("TPM: Clearing owner as specifically requested.\n")); - RETURN_ON_FAILURE(TPMClearAndReenable()); - } - - if (developer_mode) - rsf->flags |= FLAG_LAST_BOOT_DEVELOPER; - else - rsf->flags &= ~FLAG_LAST_BOOT_DEVELOPER; - - - /* If firmware space is dirty, this flushes it back to the TPM */ - if (rsf->flags != in_flags) { - VBDEBUG(("TPM: Updating firmware space.\n")); - RETURN_ON_FAILURE(WriteSpaceFirmware(rsf)); - } - - VBDEBUG(("TPM: SetupTPM() succeeded\n")); - return TPM_SUCCESS; + result = TlclAssertPhysicalPresence(); + if (result != TPM_SUCCESS) { + /* + * It is possible that the TPM was delivered with the physical + * presence command disabled. This tries enabling it, then + * tries asserting PP again. + */ + RETURN_ON_FAILURE(TlclPhysicalPresenceCMDEnable()); + RETURN_ON_FAILURE(TlclAssertPhysicalPresence()); + } + + /* Check that the TPM is enabled and activated. */ + RETURN_ON_FAILURE(TlclGetFlags(&disable, &deactivated, NULL)); + if (disable || deactivated) { + VBDEBUG(("TPM: disabled (%d) or deactivated (%d). Fixing...\n", + disable, deactivated)); + RETURN_ON_FAILURE(TlclSetEnable()); + RETURN_ON_FAILURE(TlclSetDeactivated(0)); + VBDEBUG(("TPM: Must reboot to re-enable\n")); + return TPM_E_MUST_REBOOT; + } + + /* Read the firmware space. */ + result = ReadSpaceFirmware(rsf); + if (TPM_E_BADINDEX == result) { + RollbackSpaceKernel rsk; + + /* + * This is the first time we've run, and the TPM has not been + * initialized. Initialize it. + */ + VBDEBUG(("TPM: Not initialized yet.\n")); + RETURN_ON_FAILURE(OneTimeInitializeTPM(rsf, &rsk)); + } else if (TPM_SUCCESS != result) { + VBDEBUG(("TPM: Firmware space in a bad state; giving up.\n")); + return TPM_E_CORRUPTED_STATE; + } + VBDEBUG(("TPM: Firmware space sv%d f%x v%x\n", + rsf->struct_version, rsf->flags, rsf->fw_versions)); + in_flags = rsf->flags; + + /* If we've been asked to clear the virtual dev-mode flag, do so now */ + if (disable_dev_request) { + rsf->flags &= ~FLAG_VIRTUAL_DEV_MODE_ON; + VBDEBUG(("TPM: Clearing virt dev-switch: f%x\n", rsf->flags)); + } + + /* + * The developer_mode value that's passed in is only set by a hardware + * dev-switch. We should OR it with the virtual switch, whether or not + * the virtual switch is used. If it's not used, it shouldn't change, + * so it doesn't matter. + */ + if (rsf->flags & FLAG_VIRTUAL_DEV_MODE_ON) + developer_mode = 1; + + /* + * Clear ownership if developer flag has toggled, or if an owner-clear + * has been requested. + */ + if ((developer_mode ? FLAG_LAST_BOOT_DEVELOPER : 0) != + (in_flags & FLAG_LAST_BOOT_DEVELOPER)) { + VBDEBUG(("TPM: Developer flag changed; clearing owner.\n")); + RETURN_ON_FAILURE(TPMClearAndReenable()); + } else if (clear_tpm_owner_request) { + VBDEBUG(("TPM: Clearing owner as specifically requested.\n")); + RETURN_ON_FAILURE(TPMClearAndReenable()); + } + + if (developer_mode) + rsf->flags |= FLAG_LAST_BOOT_DEVELOPER; + else + rsf->flags &= ~FLAG_LAST_BOOT_DEVELOPER; + + + /* If firmware space is dirty, flush it back to the TPM */ + if (rsf->flags != in_flags) { + VBDEBUG(("TPM: Updating firmware space.\n")); + RETURN_ON_FAILURE(WriteSpaceFirmware(rsf)); + } + + VBDEBUG(("TPM: SetupTPM() succeeded\n")); + return TPM_SUCCESS; } -/* disable MSVC warnings on unused arguments */ +/* Disable MSVC warnings on unused arguments */ __pragma(warning (disable: 4100)) #ifdef DISABLE_ROLLBACK_TPM - /* Dummy implementations which don't support TPM rollback protection */ -uint32_t RollbackS3Resume(void) { +uint32_t RollbackS3Resume(void) +{ #ifndef CHROMEOS_ENVIRONMENT - /* Initialize the TPM, but ignore return codes. In ChromeOS - * environment, don't even talk to the TPM. */ - TlclLibInit(); - TlclResume(); + /* + * Initialize the TPM, but ignore return codes. In ChromeOS + * environment, don't even talk to the TPM. + */ + TlclLibInit(); + TlclResume(); #endif - return TPM_SUCCESS; + return TPM_SUCCESS; } uint32_t RollbackFirmwareSetup(int recovery_mode, int is_hw_dev, int disable_dev_request, int clear_tpm_owner_request, - int *is_virt_dev, uint32_t *version) { + int *is_virt_dev, uint32_t *version) +{ #ifndef CHROMEOS_ENVIRONMENT - /* Initialize the TPM, but ignores return codes. In ChromeOS - * environment, don't even talk to the TPM. */ - TlclLibInit(); - TlclStartup(); - TlclContinueSelfTest(); + /* + * Initialize the TPM, but ignores return codes. In ChromeOS + * environment, don't even talk to the TPM. + */ + TlclLibInit(); + TlclStartup(); + TlclContinueSelfTest(); #endif - *version = 0; - return TPM_SUCCESS; -} - -uint32_t RollbackFirmwareRead(uint32_t* version) { - *version = 0; - return TPM_SUCCESS; + *is_virt_dev = 0; + *version = 0; + return TPM_SUCCESS; } -uint32_t RollbackFirmwareWrite(uint32_t version) { - return TPM_SUCCESS; +uint32_t RollbackFirmwareWrite(uint32_t version) +{ + return TPM_SUCCESS; } -uint32_t RollbackFirmwareLock(void) { - return TPM_SUCCESS; +uint32_t RollbackFirmwareLock(void) +{ + return TPM_SUCCESS; } -uint32_t RollbackKernelRead(uint32_t* version) { - *version = 0; - return TPM_SUCCESS; +uint32_t RollbackKernelRead(uint32_t* version) +{ + *version = 0; + return TPM_SUCCESS; } -uint32_t RollbackKernelWrite(uint32_t version) { - return TPM_SUCCESS; +uint32_t RollbackKernelWrite(uint32_t version) +{ + return TPM_SUCCESS; } -uint32_t RollbackKernelLock(void) { - return TPM_SUCCESS; +uint32_t RollbackKernelLock(void) +{ + return TPM_SUCCESS; } #else -uint32_t RollbackS3Resume(void) { - uint32_t result; - RETURN_ON_FAILURE(TlclLibInit()); - result = TlclResume(); - if (result == TPM_E_INVALID_POSTINIT) { - /* We're on a platform where the TPM maintains power in S3, so - it's already initialized. */ - return TPM_SUCCESS; - } - return result; +uint32_t RollbackS3Resume(void) +{ + uint32_t result; + RETURN_ON_FAILURE(TlclLibInit()); + result = TlclResume(); + if (result == TPM_E_INVALID_POSTINIT) { + /* + * We're on a platform where the TPM maintains power in S3, so + * it's already initialized. + */ + return TPM_SUCCESS; + } + return result; } uint32_t RollbackFirmwareSetup(int recovery_mode, int is_hw_dev, int disable_dev_request, int clear_tpm_owner_request, - int *is_virt_dev, uint32_t *version) { - RollbackSpaceFirmware rsf; - - /* Set version to 0 in case we fail */ - *version = 0; - - RETURN_ON_FAILURE(SetupTPM(recovery_mode, is_hw_dev, disable_dev_request, - clear_tpm_owner_request, &rsf)); - *version = rsf.fw_versions; - *is_virt_dev = (rsf.flags & FLAG_VIRTUAL_DEV_MODE_ON) ? 1 : 0; - VBDEBUG(("TPM: RollbackFirmwareSetup %x\n", (int)rsf.fw_versions)); - return TPM_SUCCESS; + int *is_virt_dev, uint32_t *version) +{ + RollbackSpaceFirmware rsf; + + /* Set version to 0 in case we fail */ + *version = 0; + + RETURN_ON_FAILURE(SetupTPM(recovery_mode, is_hw_dev, + disable_dev_request, + clear_tpm_owner_request, &rsf)); + *version = rsf.fw_versions; + *is_virt_dev = (rsf.flags & FLAG_VIRTUAL_DEV_MODE_ON) ? 1 : 0; + VBDEBUG(("TPM: RollbackFirmwareSetup %x\n", (int)rsf.fw_versions)); + return TPM_SUCCESS; } -uint32_t RollbackFirmwareWrite(uint32_t version) { - RollbackSpaceFirmware rsf; +uint32_t RollbackFirmwareWrite(uint32_t version) +{ + RollbackSpaceFirmware rsf; - RETURN_ON_FAILURE(ReadSpaceFirmware(&rsf)); - VBDEBUG(("TPM: RollbackFirmwareWrite %x --> %x\n", (int)rsf.fw_versions, - (int)version)); - rsf.fw_versions = version; - return WriteSpaceFirmware(&rsf); + RETURN_ON_FAILURE(ReadSpaceFirmware(&rsf)); + VBDEBUG(("TPM: RollbackFirmwareWrite %x --> %x\n", (int)rsf.fw_versions, + (int)version)); + rsf.fw_versions = version; + return WriteSpaceFirmware(&rsf); } -uint32_t RollbackFirmwareLock(void) { - return TlclSetGlobalLock(); +uint32_t RollbackFirmwareLock(void) +{ + return TlclSetGlobalLock(); } -uint32_t RollbackKernelRead(uint32_t* version) { - RollbackSpaceKernel rsk; - uint32_t perms; - - /* Read the kernel space and verify its permissions. If the kernel - * space has the wrong permission, or it doesn't contain the right - * identifier, we give up. This will need to be fixed by the - * recovery kernel. We have to worry about this because at any time - * (even with PP turned off) the TPM owner can remove and redefine a - * PP-protected space (but not write to it). */ - RETURN_ON_FAILURE(ReadSpaceKernel(&rsk)); - RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_NV_INDEX, &perms)); - if (TPM_NV_PER_PPWRITE != perms || ROLLBACK_SPACE_KERNEL_UID != rsk.uid) - return TPM_E_CORRUPTED_STATE; - - *version = rsk.kernel_versions; - VBDEBUG(("TPM: RollbackKernelRead %x\n", (int)rsk.kernel_versions)); - return TPM_SUCCESS; +uint32_t RollbackKernelRead(uint32_t* version) +{ + RollbackSpaceKernel rsk; + uint32_t perms; + + /* + * Read the kernel space and verify its permissions. If the kernel + * space has the wrong permission, or it doesn't contain the right + * identifier, we give up. This will need to be fixed by the + * recovery kernel. We have to worry about this because at any time + * (even with PP turned off) the TPM owner can remove and redefine a + * PP-protected space (but not write to it). + */ + RETURN_ON_FAILURE(ReadSpaceKernel(&rsk)); + RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_NV_INDEX, &perms)); + if (TPM_NV_PER_PPWRITE != perms || ROLLBACK_SPACE_KERNEL_UID != rsk.uid) + return TPM_E_CORRUPTED_STATE; + + *version = rsk.kernel_versions; + VBDEBUG(("TPM: RollbackKernelRead %x\n", (int)rsk.kernel_versions)); + return TPM_SUCCESS; } -uint32_t RollbackKernelWrite(uint32_t version) { - RollbackSpaceKernel rsk; - RETURN_ON_FAILURE(ReadSpaceKernel(&rsk)); - VBDEBUG(("TPM: RollbackKernelWrite %x --> %x\n", (int)rsk.kernel_versions, - (int)version)); - rsk.kernel_versions = version; - return WriteSpaceKernel(&rsk); +uint32_t RollbackKernelWrite(uint32_t version) +{ + RollbackSpaceKernel rsk; + RETURN_ON_FAILURE(ReadSpaceKernel(&rsk)); + VBDEBUG(("TPM: RollbackKernelWrite %x --> %x\n", + (int)rsk.kernel_versions, (int)version)); + rsk.kernel_versions = version; + return WriteSpaceKernel(&rsk); } -uint32_t RollbackKernelLock(void) { - if (g_rollback_recovery_mode) { - return TPM_SUCCESS; - } else { - return TlclLockPhysicalPresence(); - } +uint32_t RollbackKernelLock(void) +{ + if (g_rollback_recovery_mode) + return TPM_SUCCESS; + else + return TlclLockPhysicalPresence(); } -#endif // DISABLE_ROLLBACK_TPM +#endif /* DISABLE_ROLLBACK_TPM */ diff --git a/firmware/lib/vboot_common.c b/firmware/lib/vboot_common.c index 7878701f..bf79b81f 100644 --- a/firmware/lib/vboot_common.c +++ b/firmware/lib/vboot_common.c @@ -173,28 +173,6 @@ int VerifyDigest(const uint8_t* digest, const VbSignature *sig, } -int EqualData(const uint8_t* data, uint64_t size, const VbSignature *hash, - const RSAPublicKey* key) { - uint8_t* digest = NULL; - int rv; - - if (hash->sig_size != hash_size_map[key->algorithm]) { - VBDEBUG(("Wrong hash size for algorithm.\n")); - return 1; - } - if (hash->data_size > size) { - VBDEBUG(("Data buffer smaller than length of signed data.\n")); - return 1; - } - - digest = DigestBuf(data, hash->data_size, key->algorithm); - - rv = SafeMemcmp(digest, GetSignatureDataC(hash), hash->sig_size); - VbExFree(digest); - return rv; -} - - int KeyBlockVerify(const VbKeyBlockHeader* block, uint64_t size, const VbPublicKey *key, int hash_only) { @@ -313,63 +291,6 @@ int KeyBlockVerify(const VbKeyBlockHeader* block, uint64_t size, return VBOOT_SUCCESS; } - -int VerifyECPreamble(const VbECPreambleHeader* preamble, - uint64_t size, const RSAPublicKey* key) { - - const VbSignature* sig = &preamble->preamble_signature; - - /* Sanity checks before attempting signature of data */ - if(size < EXPECTED_VB_EC_PREAMBLE_HEADER1_0_SIZE) { - VBDEBUG(("Not enough data for EC preamble header.\n")); - return VBOOT_PREAMBLE_INVALID; - } - if (preamble->header_version_major != - EC_PREAMBLE_HEADER_VERSION_MAJOR) { - VBDEBUG(("Incompatible EC preamble header version (%d, not %d).\n", - preamble->header_version_major, - EC_PREAMBLE_HEADER_VERSION_MAJOR)); - return VBOOT_PREAMBLE_INVALID; - } - if (size < preamble->preamble_size) { - VBDEBUG(("Not enough data for EC preamble.\n")); - return VBOOT_PREAMBLE_INVALID; - } - - /* Check signature */ - if (VerifySignatureInside(preamble, preamble->preamble_size, sig)) { - VBDEBUG(("EC preamble signature off end of preamble\n")); - return VBOOT_PREAMBLE_INVALID; - } - - /* Make sure advertised signature data sizes are sane. */ - if (preamble->preamble_size < sig->data_size) { - VBDEBUG(("EC signature calculated past end of the block\n")); - return VBOOT_PREAMBLE_INVALID; - } - - if (VerifyData((const uint8_t*)preamble, size, sig, key)) { - VBDEBUG(("EC preamble signature validation failed\n")); - return VBOOT_PREAMBLE_SIGNATURE; - } - - /* Verify we signed enough data */ - if (sig->data_size < sizeof(VbFirmwarePreambleHeader)) { - VBDEBUG(("Didn't sign enough data\n")); - return VBOOT_PREAMBLE_INVALID; - } - - /* Verify body digest is inside the signed data */ - if (VerifySignatureInside(preamble, sig->data_size, - &preamble->body_digest)) { - VBDEBUG(("EC body digest off end of preamble\n")); - return VBOOT_PREAMBLE_INVALID; - } - - /* Success */ - return VBOOT_SUCCESS; -} - int VerifyFirmwarePreamble(const VbFirmwarePreambleHeader* preamble, uint64_t size, const RSAPublicKey* key) { diff --git a/firmware/lib/vboot_display.c b/firmware/lib/vboot_display.c index d0250bf3..929cacc2 100644 --- a/firmware/lib/vboot_display.c +++ b/firmware/lib/vboot_display.c @@ -402,8 +402,7 @@ static void FillInSha1Sum(char *outbuf, VbPublicKey* key) { VbExFree(digest); } - -static const char *RecoveryReasonString(uint8_t code) { +const char *RecoveryReasonString(uint8_t code) { switch(code) { case VBNV_RECOVERY_NOT_REQUESTED: return "Recovery not requested"; diff --git a/futility/futility.c b/futility/futility.c index a6a8aa39..483f4521 100644 --- a/futility/futility.c +++ b/futility/futility.c @@ -6,25 +6,24 @@ #define _GNU_SOURCE #include <errno.h> +#include <fcntl.h> #include <limits.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> #include <unistd.h> #include "futility.h" -/* #define DEBUG 1 */ -#ifdef DEBUG -#define debug(args...) printf(args) -#else -#define debug(args...) -#endif - #define MYNAME "futility" #define SUBDIR "old_bins" +#define LOGFILE "/tmp/futility.log" + +/******************************************************************************/ + static const char * const usage= "\n\ Usage: " MYNAME " PROGRAM|COMMAND [args...]\n\ \n\ @@ -70,6 +69,81 @@ static int help(int argc, char *argv[]) DECLARE_FUTIL_COMMAND(help, help, "Show a bit of help"); +/******************************************************************************/ +/* Logging stuff */ + +static int log_fd = -1; + +/* Write the string and a newline. Silently give up on errors */ +static void log_str(char *str) +{ + int len, done, n; + + if (log_fd < 0) + return; + + if (!str) + str = "(NULL)"; + + len = strlen(str); + if (len == 0) { + str = "(EMPTY)"; + len = strlen(str); + } + + for (done = 0; done < len; done += n) { + n = write(log_fd, str+done, len-done); + if (n < 0) + return; + } + + write(log_fd, "\n", 1); +} + +static void log_close(void) +{ + if (log_fd >= 0) + close(log_fd); + log_fd = -1; +} + +static void log_open(void) +{ + struct flock lock; + int ret; + + log_fd = open(LOGFILE, O_WRONLY|O_APPEND|O_CREAT, 0666); + if (log_fd < 0) { + + if (errno != EACCES) + return; + + /* Permission problems should improve shortly ... */ + sleep(1); + log_fd = open(LOGFILE, O_WRONLY|O_APPEND|O_CREAT, 0666); + if (log_fd < 0) /* Nope, they didn't */ + return; + } + + /* Let anyone have a turn */ + fchmod(log_fd, 0666); + + /* But only one at a time */ + memset(&lock, 0, sizeof(lock)); + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_END; + + ret = fcntl(log_fd, F_SETLKW, &lock); /* this blocks */ + if (ret < 0) + log_close(); + + /* delimiter */ + log_str("##### HEY #####"); +} + +/******************************************************************************/ +/* Here we go */ + int main(int argc, char *argv[], char *envp[]) { char *progname; @@ -82,8 +156,9 @@ int main(int argc, char *argv[], char *envp[]) int i; futil_cmd_t *cmd; + log_open(); for (i = 0; i < argc; i++) - debug("argv[%d] = %s\n", i, argv[i]); + log_str(argv[i]); /* How were we invoked? */ progname = strrchr(argv[0], '/'); @@ -91,7 +166,6 @@ int main(int argc, char *argv[], char *envp[]) progname++; else progname = argv[0]; - debug("progname is %s\n", progname); /* Invoked directly by name */ if (0 == strcmp(progname, MYNAME)) { @@ -110,7 +184,6 @@ int main(int argc, char *argv[], char *envp[]) progname++; else progname = argv[0]; - debug("now progname is %s\n", progname); } /* See if it's asking for something we know how to do ourselves */ @@ -129,7 +202,6 @@ int main(int argc, char *argv[], char *envp[]) buf, strerror(errno)); exit(1); } - debug("truename is %s\n", truename); s = strrchr(truename, '/'); /* Find the true directory */ if (s) { *s = '\0'; @@ -140,10 +212,6 @@ int main(int argc, char *argv[], char *envp[]) /* We've allocated PATH_MAX. If the old binary path doesn't fit, it can't be * in the filesystem. */ snprintf(oldname, PATH_MAX, "%s/%s/%s", truename, SUBDIR, progname); - debug("oldname is %s\n", oldname); - - for (i = 0; i < argc; i++) - debug("argv[%d] = %s\n", i, argv[i]); fflush(0); execve(oldname, argv, envp); diff --git a/host/arch/amd64/lib/crossystem_arch.c b/host/arch/x86_64/lib/crossystem_arch.c index 1f5d6522..1f5d6522 120000 --- a/host/arch/amd64/lib/crossystem_arch.c +++ b/host/arch/x86_64/lib/crossystem_arch.c diff --git a/tests/cgptlib_test.c b/tests/cgptlib_test.c index 3402ac85..b48543f3 100644 --- a/tests/cgptlib_test.c +++ b/tests/cgptlib_test.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. */ @@ -13,7 +13,8 @@ #include "test_common.h" #include "utility.h" -/* Testing partition layout (sector_bytes=512) +/* + * Testing partition layout (sector_bytes=512) * * LBA Size Usage * --------------------------------------------------------- @@ -44,1217 +45,1396 @@ static const Guid guid_zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}}; static const Guid guid_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; static const Guid guid_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS; -/* Copy a random-for-this-program-only Guid into the dest. The num parameter +/* + * Copy a random-for-this-program-only Guid into the dest. The num parameter * completely determines the Guid. */ -static void SetGuid(void *dest, uint32_t num) { - Guid g = {{{num,0xd450,0x44bc,0xa6,0x93,{0xb8,0xac,0x75,0x5f,0xcd,0x48}}}}; - Memcpy(dest, &g, sizeof(Guid)); +static void SetGuid(void *dest, uint32_t num) +{ + Guid g = {{{num,0xd450,0x44bc,0xa6,0x93, + {0xb8,0xac,0x75,0x5f,0xcd,0x48}}}}; + Memcpy(dest, &g, sizeof(Guid)); } -/* 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. */ -static void RefreshCrc32(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 = - Crc32((uint8_t*)entries, - header->number_of_entries * header->size_of_entry); - header->header_crc32 = 0; - header->header_crc32 = Crc32((uint8_t*)header, header->size); - header2->entries_crc32 = - Crc32((uint8_t*)entries2, - header2->number_of_entries * header2->size_of_entry); - header2->header_crc32 = 0; - header2->header_crc32 = Crc32((uint8_t*)header2, header2->size); +/* + * 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. + */ +static void RefreshCrc32(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 = + Crc32((uint8_t *)entries, + header->number_of_entries * header->size_of_entry); + header->header_crc32 = 0; + header->header_crc32 = Crc32((uint8_t *)header, header->size); + header2->entries_crc32 = + Crc32((uint8_t *)entries2, + header2->number_of_entries * header2->size_of_entry); + header2->header_crc32 = 0; + header2->header_crc32 = Crc32((uint8_t *)header2, header2->size); } - -static void ZeroHeaders(GptData* gpt) { - Memset(gpt->primary_header, 0, MAX_SECTOR_SIZE); - Memset(gpt->secondary_header, 0, MAX_SECTOR_SIZE); +static void ZeroHeaders(GptData *gpt) +{ + Memset(gpt->primary_header, 0, MAX_SECTOR_SIZE); + Memset(gpt->secondary_header, 0, MAX_SECTOR_SIZE); } - -static void ZeroEntries(GptData* gpt) { - Memset(gpt->primary_entries, 0, PARTITION_ENTRIES_SIZE); - Memset(gpt->secondary_entries, 0, PARTITION_ENTRIES_SIZE); +static void ZeroEntries(GptData *gpt) +{ + Memset(gpt->primary_entries, 0, PARTITION_ENTRIES_SIZE); + Memset(gpt->secondary_entries, 0, PARTITION_ENTRIES_SIZE); } - -static void ZeroHeadersEntries(GptData* gpt) { - ZeroHeaders(gpt); - ZeroEntries(gpt); +static void ZeroHeadersEntries(GptData *gpt) +{ + ZeroHeaders(gpt); + ZeroEntries(gpt); } - -/* Returns a pointer to a static GptData instance (no free is required). +/* + * Return 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. */ -static GptData* GetEmptyGptData() { - static GptData 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)); - gpt.primary_header = primary_header; - gpt.primary_entries = primary_entries; - gpt.secondary_header = secondary_header; - gpt.secondary_entries = secondary_entries; - ZeroHeadersEntries(&gpt); - - /* Initialize GptData internal states. */ - gpt.current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; - - return &gpt; + * All content of headers and entries are zero. + */ +static GptData *GetEmptyGptData(void) +{ + static GptData 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)); + gpt.primary_header = primary_header; + gpt.primary_entries = primary_entries; + gpt.secondary_header = secondary_header; + gpt.secondary_entries = secondary_entries; + ZeroHeadersEntries(&gpt); + + /* Initialize GptData internal states. */ + gpt.current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; + + return &gpt; } - -/* Fills in most of fields and creates the layout described in the top of this +/* + * Fill in most of fields and creates the layout described in the top of this * file. Before calling this function, primary/secondary header/entries must * have been pointed to the buffer, say, a gpt returned from GetEmptyGptData(). * This function returns a good (valid) copy of GPT layout described in top of - * this file. */ -static void BuildTestGptData(GptData* gpt) { - GptHeader *header, *header2; - GptEntry *entries, *entries2; - Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; - Guid chromeos_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS; - - gpt->sector_bytes = DEFAULT_SECTOR_SIZE; - gpt->drive_sectors = DEFAULT_DRIVE_SECTORS; - gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; - gpt->valid_headers = MASK_BOTH; - gpt->valid_entries = MASK_BOTH; - gpt->modified = 0; - - /* 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); - header->reserved_zero = 0; - header->my_lba = 1; - header->alternate_lba = DEFAULT_DRIVE_SECTORS - 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)); - SetGuid(&entries[0].unique, 0); - entries[0].starting_lba = 34; - entries[0].ending_lba = 133; - Memcpy(&entries[1].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); - SetGuid(&entries[1].unique, 1); - entries[1].starting_lba = 134; - entries[1].ending_lba = 232; - Memcpy(&entries[2].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); - SetGuid(&entries[2].unique, 2); - entries[2].starting_lba = 234; - entries[2].ending_lba = 331; - Memcpy(&entries[3].type, &chromeos_kernel, sizeof(chromeos_kernel)); - SetGuid(&entries[3].unique, 3); - entries[3].starting_lba = 334; - entries[3].ending_lba = 430; - - /* build secondary */ - header2 = (GptHeader*)gpt->secondary_header; - entries2 = (GptEntry*)gpt->secondary_entries; - Memcpy(header2, header, sizeof(GptHeader)); - Memcpy(entries2, entries, PARTITION_ENTRIES_SIZE); - header2->my_lba = DEFAULT_DRIVE_SECTORS - 1; /* 466 */ - header2->alternate_lba = 1; - header2->entries_lba = DEFAULT_DRIVE_SECTORS - 1 - 32; /* 434 */ - - RefreshCrc32(gpt); + * this file. + */ +static void BuildTestGptData(GptData *gpt) +{ + GptHeader *header, *header2; + GptEntry *entries, *entries2; + Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; + Guid chromeos_rootfs = GPT_ENT_TYPE_CHROMEOS_ROOTFS; + + gpt->sector_bytes = DEFAULT_SECTOR_SIZE; + gpt->drive_sectors = DEFAULT_DRIVE_SECTORS; + gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; + gpt->valid_headers = MASK_BOTH; + gpt->valid_entries = MASK_BOTH; + gpt->modified = 0; + + /* 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); + header->reserved_zero = 0; + header->my_lba = 1; + header->alternate_lba = DEFAULT_DRIVE_SECTORS - 1; + header->first_usable_lba = 34; + header->last_usable_lba = DEFAULT_DRIVE_SECTORS - 1 - 32 - 1; /* 433 */ + header->entries_lba = 2; + /* 512B / 128B * 32sectors = 128 entries */ + header->number_of_entries = 128; + header->size_of_entry = 128; /* bytes */ + Memcpy(&entries[0].type, &chromeos_kernel, sizeof(chromeos_kernel)); + SetGuid(&entries[0].unique, 0); + entries[0].starting_lba = 34; + entries[0].ending_lba = 133; + Memcpy(&entries[1].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); + SetGuid(&entries[1].unique, 1); + entries[1].starting_lba = 134; + entries[1].ending_lba = 232; + Memcpy(&entries[2].type, &chromeos_rootfs, sizeof(chromeos_rootfs)); + SetGuid(&entries[2].unique, 2); + entries[2].starting_lba = 234; + entries[2].ending_lba = 331; + Memcpy(&entries[3].type, &chromeos_kernel, sizeof(chromeos_kernel)); + SetGuid(&entries[3].unique, 3); + entries[3].starting_lba = 334; + entries[3].ending_lba = 430; + + /* Build secondary */ + header2 = (GptHeader *)gpt->secondary_header; + entries2 = (GptEntry *)gpt->secondary_entries; + Memcpy(header2, header, sizeof(GptHeader)); + Memcpy(entries2, entries, PARTITION_ENTRIES_SIZE); + header2->my_lba = DEFAULT_DRIVE_SECTORS - 1; /* 466 */ + header2->alternate_lba = 1; + header2->entries_lba = DEFAULT_DRIVE_SECTORS - 1 - 32; /* 434 */ + + RefreshCrc32(gpt); } -/* Tests if the structures are the expected size; if this fails, - * struct packing is not working properly. */ -static int StructSizeTest() { - - EXPECT(GUID_EXPECTED_SIZE == sizeof(Guid)); - EXPECT(GPTHEADER_EXPECTED_SIZE == sizeof(GptHeader)); - EXPECT(GPTENTRY_EXPECTED_SIZE == sizeof(GptEntry)); +/* + * Test if the structures are the expected size; if this fails, struct packing + * is not working properly. + */ +static int StructSizeTest(void) +{ - return TEST_OK; + EXPECT(GUID_EXPECTED_SIZE == sizeof(Guid)); + EXPECT(GPTHEADER_EXPECTED_SIZE == sizeof(GptHeader)); + EXPECT(GPTENTRY_EXPECTED_SIZE == sizeof(GptEntry)); + return TEST_OK; } -/* Tests if the default structure returned by BuildTestGptData() is good. */ -static int TestBuildTestGptData() { - GptData* gpt; +/* Test if the default structure returned by BuildTestGptData() is good. */ +static int TestBuildTestGptData(void) +{ + GptData *gpt; - gpt = GetEmptyGptData(); - BuildTestGptData(gpt); - EXPECT(GPT_SUCCESS == GptInit(gpt)); - return TEST_OK; + gpt = GetEmptyGptData(); + BuildTestGptData(gpt); + EXPECT(GPT_SUCCESS == GptInit(gpt)); + gpt->sector_bytes = 0; + EXPECT(GPT_ERROR_INVALID_SECTOR_SIZE == GptInit(gpt)); + return TEST_OK; } - -/* Tests if wrong sector_bytes or drive_sectors is detected by GptInit(). - * Currently we only support 512 bytes per sector. - * In the future, we may support other sizes. - * A too small drive_sectors should be rejected by GptInit(). */ -static int ParameterTests() { - GptData* gpt; - struct { - uint32_t sector_bytes; - uint64_t drive_sectors; - int expected_retval; - } cases[] = { - {512, DEFAULT_DRIVE_SECTORS, GPT_SUCCESS}, - {520, DEFAULT_DRIVE_SECTORS, GPT_ERROR_INVALID_SECTOR_SIZE}, - {512, 0, GPT_ERROR_INVALID_SECTOR_NUMBER}, - {512, 66, GPT_ERROR_INVALID_SECTOR_NUMBER}, - {512, GPT_PMBR_SECTOR + GPT_HEADER_SECTOR * 2 + GPT_ENTRIES_SECTORS * 2, - GPT_SUCCESS}, - {4096, DEFAULT_DRIVE_SECTORS, GPT_ERROR_INVALID_SECTOR_SIZE}, - }; - int i; - - gpt = GetEmptyGptData(); - for (i = 0; i < ARRAY_SIZE(cases); ++i) { - BuildTestGptData(gpt); - gpt->sector_bytes = cases[i].sector_bytes; - gpt->drive_sectors = cases[i].drive_sectors; - EXPECT(cases[i].expected_retval == CheckParameters(gpt)); - } - - return TEST_OK; +/* + * Test if wrong sector_bytes or drive_sectors is detected by GptInit(). + * Currently we only support 512 bytes per sector. In the future, we may + * support other sizes. A too small drive_sectors should be rejected by + * GptInit(). + */ +static int ParameterTests(void) +{ + GptData *gpt; + struct { + uint32_t sector_bytes; + uint64_t drive_sectors; + int expected_retval; + } cases[] = { + {512, DEFAULT_DRIVE_SECTORS, GPT_SUCCESS}, + {520, DEFAULT_DRIVE_SECTORS, GPT_ERROR_INVALID_SECTOR_SIZE}, + {512, 0, GPT_ERROR_INVALID_SECTOR_NUMBER}, + {512, 66, GPT_ERROR_INVALID_SECTOR_NUMBER}, + {512, GPT_PMBR_SECTOR + GPT_HEADER_SECTOR * 2 + + GPT_ENTRIES_SECTORS * 2, GPT_SUCCESS}, + {4096, DEFAULT_DRIVE_SECTORS, GPT_ERROR_INVALID_SECTOR_SIZE}, + }; + int i; + + gpt = GetEmptyGptData(); + for (i = 0; i < ARRAY_SIZE(cases); ++i) { + BuildTestGptData(gpt); + gpt->sector_bytes = cases[i].sector_bytes; + gpt->drive_sectors = cases[i].drive_sectors; + EXPECT(cases[i].expected_retval == CheckParameters(gpt)); + } + + return TEST_OK; } +/* Test if header CRC in two copies are calculated. */ +static int HeaderCrcTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; -/* Tests if header CRC in two copies are calculated. */ -static int HeaderCrcTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; + BuildTestGptData(gpt); + EXPECT(HeaderCrc(h1) == h1->header_crc32); - BuildTestGptData(gpt); - EXPECT(HeaderCrc(h1) == h1->header_crc32); + /* CRC covers first byte of header */ + BuildTestGptData(gpt); + gpt->primary_header[0] ^= 0xa5; + EXPECT(HeaderCrc(h1) != h1->header_crc32); - /* CRC covers first byte of header */ - BuildTestGptData(gpt); - gpt->primary_header[0] ^= 0xa5; - EXPECT(HeaderCrc(h1) != h1->header_crc32); + /* CRC covers last byte of header */ + BuildTestGptData(gpt); + gpt->primary_header[h1->size - 1] ^= 0x5a; + EXPECT(HeaderCrc(h1) != h1->header_crc32); - /* CRC covers last byte of header */ - BuildTestGptData(gpt); - gpt->primary_header[h1->size - 1] ^= 0x5a; - EXPECT(HeaderCrc(h1) != h1->header_crc32); + /* CRC only covers header */ + BuildTestGptData(gpt); + gpt->primary_header[h1->size] ^= 0x5a; + EXPECT(HeaderCrc(h1) == h1->header_crc32); - /* CRC only covers header */ - BuildTestGptData(gpt); - gpt->primary_header[h1->size] ^= 0x5a; - EXPECT(HeaderCrc(h1) == h1->header_crc32); - - return TEST_OK; + return TEST_OK; } +/* Test if header-same comparison works. */ +static int HeaderSameTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptHeader *h2 = (GptHeader *)gpt->secondary_header; + GptHeader h3; -/* Tests if signature ("EFI PART") is checked. */ -static int SignatureTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - GptHeader* h2 = (GptHeader*)gpt->secondary_header; - int i; + EXPECT(0 == HeaderFieldsSame(h1, h2)); - 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)); - } + Memcpy(&h3, h2, sizeof(h3)); + h3.signature[0] ^= 0xba; + EXPECT(1 == HeaderFieldsSame(h1, &h3)); - return TEST_OK; -} + Memcpy(&h3, h2, sizeof(h3)); + h3.revision++; + EXPECT(1 == HeaderFieldsSame(h1, &h3)); + Memcpy(&h3, h2, sizeof(h3)); + h3.size++; + EXPECT(1 == HeaderFieldsSame(h1, &h3)); -/* The revision we currently support is GPT_HEADER_REVISION. - * If the revision in header is not that, we expect the header is invalid. */ -static int RevisionTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - GptHeader* h2 = (GptHeader*)gpt->secondary_header; - int i; - - struct { - uint32_t value_to_test; - int expect_rv; - } cases[] = { - {0x01000000, 1}, - {0x00010000, 0}, /* GPT_HEADER_REVISION */ - {0x00000100, 1}, - {0x00000001, 1}, - {0x23010456, 1}, - }; - - for (i = 0; i < ARRAY_SIZE(cases); ++i) { - BuildTestGptData(gpt); - h1->revision = cases[i].value_to_test; - h2->revision = cases[i].value_to_test; - RefreshCrc32(gpt); - - EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == cases[i].expect_rv); - EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == cases[i].expect_rv); - } - return TEST_OK; -} + Memcpy(&h3, h2, sizeof(h3)); + h3.reserved_zero++; + EXPECT(1 == HeaderFieldsSame(h1, &h3)); + Memcpy(&h3, h2, sizeof(h3)); + h3.first_usable_lba++; + EXPECT(1 == HeaderFieldsSame(h1, &h3)); -static int SizeTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - GptHeader* h2 = (GptHeader*)gpt->secondary_header; - int i; - - struct { - uint32_t value_to_test; - int expect_rv; - } cases[] = { - {91, 1}, - {92, 0}, - {93, 0}, - {511, 0}, - {512, 0}, - {513, 1}, - }; - - for (i = 0; i < ARRAY_SIZE(cases); ++i) { - BuildTestGptData(gpt); - h1->size = cases[i].value_to_test; - h2->size = cases[i].value_to_test; - RefreshCrc32(gpt); - - EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == cases[i].expect_rv); - EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == cases[i].expect_rv); - } - return TEST_OK; -} + Memcpy(&h3, h2, sizeof(h3)); + h3.last_usable_lba++; + EXPECT(1 == HeaderFieldsSame(h1, &h3)); + Memcpy(&h3, h2, sizeof(h3)); + h3.disk_uuid.u.raw[0] ^= 0xba; + EXPECT(1 == HeaderFieldsSame(h1, &h3)); -/* Tests if CRC is checked. */ -static int CrcFieldTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - GptHeader* h2 = (GptHeader*)gpt->secondary_header; - - BuildTestGptData(gpt); - /* 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)); - /* Refresh the CRC; should pass now */ - RefreshCrc32(gpt); - EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors)); - EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors)); - - return TEST_OK; -} + Memcpy(&h3, h2, sizeof(h3)); + h3.number_of_entries++; + EXPECT(1 == HeaderFieldsSame(h1, &h3)); + Memcpy(&h3, h2, sizeof(h3)); + h3.size_of_entry++; + EXPECT(1 == HeaderFieldsSame(h1, &h3)); -/* Tests if reserved fields are checked. - * We'll try non-zero values to test. */ -static int ReservedFieldsTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - GptHeader* h2 = (GptHeader*)gpt->secondary_header; + Memcpy(&h3, h2, sizeof(h3)); + h3.entries_crc32++; + EXPECT(1 == HeaderFieldsSame(h1, &h3)); - BuildTestGptData(gpt); - 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)); + return TEST_OK; +} -#ifdef PADDING_CHECKED - /* TODO: padding check is currently disabled */ - BuildTestGptData(gpt); - 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)); -#endif +/* Test if signature ("EFI PART") is checked. */ +static int SignatureTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptHeader *h2 = (GptHeader *)gpt->secondary_header; + int i; + + EXPECT(1 == CheckHeader(NULL, 0, gpt->drive_sectors)); + + 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)); + } + + return TEST_OK; +} - return TEST_OK; +/* + * The revision we currently support is GPT_HEADER_REVISION. If the revision + * in header is not that, we expect the header is invalid. + */ +static int RevisionTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptHeader *h2 = (GptHeader *)gpt->secondary_header; + int i; + + struct { + uint32_t value_to_test; + int expect_rv; + } cases[] = { + {0x01000000, 1}, + {0x00010000, 0}, /* GPT_HEADER_REVISION */ + {0x00000100, 1}, + {0x00000001, 1}, + {0x23010456, 1}, + }; + + for (i = 0; i < ARRAY_SIZE(cases); ++i) { + BuildTestGptData(gpt); + h1->revision = cases[i].value_to_test; + h2->revision = cases[i].value_to_test; + RefreshCrc32(gpt); + + EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == + cases[i].expect_rv); + EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == + cases[i].expect_rv); + } + return TEST_OK; } +static int SizeTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptHeader *h2 = (GptHeader *)gpt->secondary_header; + int i; + + struct { + uint32_t value_to_test; + int expect_rv; + } cases[] = { + {91, 1}, + {92, 0}, + {93, 0}, + {511, 0}, + {512, 0}, + {513, 1}, + }; + + for (i = 0; i < ARRAY_SIZE(cases); ++i) { + BuildTestGptData(gpt); + h1->size = cases[i].value_to_test; + h2->size = cases[i].value_to_test; + RefreshCrc32(gpt); + + EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == + cases[i].expect_rv); + EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == + cases[i].expect_rv); + } + return TEST_OK; +} -/* Technically, any size which is 2^N where N > 6 should work, but our - * library only supports one size. */ -static int SizeOfPartitionEntryTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - GptHeader* h2 = (GptHeader*)gpt->secondary_header; - int i; - - struct { - uint32_t value_to_test; - int expect_rv; - } cases[] = { - {127, 1}, - {128, 0}, - {129, 1}, - {256, 1}, - {512, 1}, - }; - - /* Check size of entryes */ - for (i = 0; i < ARRAY_SIZE(cases); ++i) { - BuildTestGptData(gpt); - h1->size_of_entry = cases[i].value_to_test; - h2->size_of_entry = cases[i].value_to_test; - h1->number_of_entries = TOTAL_ENTRIES_SIZE / cases[i].value_to_test; - h2->number_of_entries = TOTAL_ENTRIES_SIZE / cases[i].value_to_test; - RefreshCrc32(gpt); - - EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == cases[i].expect_rv); - EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == cases[i].expect_rv); - } - - return TEST_OK; +/* Test if CRC is checked. */ +static int CrcFieldTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptHeader *h2 = (GptHeader *)gpt->secondary_header; + + BuildTestGptData(gpt); + /* 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)); + /* Refresh the CRC; should pass now */ + RefreshCrc32(gpt); + EXPECT(0 == CheckHeader(h1, 0, gpt->drive_sectors)); + EXPECT(0 == CheckHeader(h2, 1, gpt->drive_sectors)); + + return TEST_OK; } +/* Test if reserved fields are checked. We'll try non-zero values to test. */ +static int ReservedFieldsTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptHeader *h2 = (GptHeader *)gpt->secondary_header; -/* Technically, any size which is 2^N where N > 6 should work, but our - * library only supports one size. */ -static int NumberOfPartitionEntriesTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - GptHeader* h2 = (GptHeader*)gpt->secondary_header; + BuildTestGptData(gpt); + 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)); - BuildTestGptData(gpt); - 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)); +#ifdef PADDING_CHECKED + /* TODO: padding check is currently disabled */ + BuildTestGptData(gpt); + 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)); +#endif - return TEST_OK; + return TEST_OK; } +/* + * Technically, any size which is 2^N where N > 6 should work, but our + * library only supports one size. + */ +static int SizeOfPartitionEntryTest(void) { + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptHeader *h2 = (GptHeader *)gpt->secondary_header; + int i; + + struct { + uint32_t value_to_test; + int expect_rv; + } cases[] = { + {127, 1}, + {128, 0}, + {129, 1}, + {256, 1}, + {512, 1}, + }; + + /* Check size of entryes */ + for (i = 0; i < ARRAY_SIZE(cases); ++i) { + BuildTestGptData(gpt); + h1->size_of_entry = cases[i].value_to_test; + h2->size_of_entry = cases[i].value_to_test; + h1->number_of_entries = TOTAL_ENTRIES_SIZE / + cases[i].value_to_test; + h2->number_of_entries = TOTAL_ENTRIES_SIZE / + cases[i].value_to_test; + RefreshCrc32(gpt); + + EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == + cases[i].expect_rv); + EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == + cases[i].expect_rv); + } + + return TEST_OK; +} -/* Tests if myLBA field is checked (1 for primary, last for secondary). */ -static int MyLbaTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - GptHeader* h2 = (GptHeader*)gpt->secondary_header; - - /* 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)); - - 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)); - - 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)); - - /* 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)); - - 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)); - - 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)); - - 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)); - - return TEST_OK; +/* + * Technically, any size which is 2^N where N > 6 should work, but our library + * only supports one size. + */ +static int NumberOfPartitionEntriesTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptHeader *h2 = (GptHeader *)gpt->secondary_header; + + BuildTestGptData(gpt); + 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)); + + return TEST_OK; } -/* Tests if FirstUsableLBA and LastUsableLBA are checked. +/* Test if myLBA field is checked (1 for primary, last for secondary). */ +static int MyLbaTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptHeader *h2 = (GptHeader *)gpt->secondary_header; + + /* 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)); + + 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)); + + 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)); + + /* 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)); + + 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)); + + 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)); + + 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)); + + return TEST_OK; +} + +/* 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. */ -static int FirstUsableLbaAndLastUsableLbaTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - GptHeader* h2 = (GptHeader*)gpt->secondary_header; - int i; - - struct { - uint64_t primary_entries_lba; - uint64_t primary_first_usable_lba; - uint64_t primary_last_usable_lba; - uint64_t secondary_first_usable_lba; - uint64_t secondary_last_usable_lba; - uint64_t secondary_entries_lba; - int primary_rv; - int secondary_rv; - } cases[] = { - {2, 34, 433, 34, 433, 434, 0, 0}, - {2, 34, 432, 34, 430, 434, 0, 0}, - {2, 33, 433, 33, 433, 434, 1, 1}, - {2, 34, 434, 34, 433, 434, 1, 0}, - {2, 34, 433, 34, 434, 434, 0, 1}, - {2, 35, 433, 35, 433, 434, 0, 0}, - {2, 433, 433, 433, 433, 434, 0, 0}, - {2, 434, 433, 434, 434, 434, 1, 1}, - {2, 433, 34, 34, 433, 434, 1, 0}, - {2, 34, 433, 433, 34, 434, 0, 1}, - }; - - for (i = 0; i < ARRAY_SIZE(cases); ++i) { - BuildTestGptData(gpt); - h1->entries_lba = cases[i].primary_entries_lba; - h1->first_usable_lba = cases[i].primary_first_usable_lba; - h1->last_usable_lba = cases[i].primary_last_usable_lba; - h2->entries_lba = cases[i].secondary_entries_lba; - h2->first_usable_lba = cases[i].secondary_first_usable_lba; - h2->last_usable_lba = cases[i].secondary_last_usable_lba; - RefreshCrc32(gpt); - - EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == cases[i].primary_rv); - EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == cases[i].secondary_rv); - } - - return TEST_OK; +static int FirstUsableLbaAndLastUsableLbaTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptHeader *h2 = (GptHeader *)gpt->secondary_header; + int i; + + struct { + uint64_t primary_entries_lba; + uint64_t primary_first_usable_lba; + uint64_t primary_last_usable_lba; + uint64_t secondary_first_usable_lba; + uint64_t secondary_last_usable_lba; + uint64_t secondary_entries_lba; + int primary_rv; + int secondary_rv; + } cases[] = { + {2, 34, 433, 34, 433, 434, 0, 0}, + {2, 34, 432, 34, 430, 434, 0, 0}, + {2, 33, 433, 33, 433, 434, 1, 1}, + {2, 34, 434, 34, 433, 434, 1, 0}, + {2, 34, 433, 34, 434, 434, 0, 1}, + {2, 35, 433, 35, 433, 434, 0, 0}, + {2, 433, 433, 433, 433, 434, 0, 0}, + {2, 434, 433, 434, 434, 434, 1, 1}, + {2, 433, 34, 34, 433, 434, 1, 0}, + {2, 34, 433, 433, 34, 434, 0, 1}, + }; + + for (i = 0; i < ARRAY_SIZE(cases); ++i) { + BuildTestGptData(gpt); + h1->entries_lba = cases[i].primary_entries_lba; + h1->first_usable_lba = cases[i].primary_first_usable_lba; + h1->last_usable_lba = cases[i].primary_last_usable_lba; + h2->entries_lba = cases[i].secondary_entries_lba; + h2->first_usable_lba = cases[i].secondary_first_usable_lba; + h2->last_usable_lba = cases[i].secondary_last_usable_lba; + RefreshCrc32(gpt); + + EXPECT(CheckHeader(h1, 0, gpt->drive_sectors) == + cases[i].primary_rv); + EXPECT(CheckHeader(h2, 1, gpt->drive_sectors) == + cases[i].secondary_rv); + } + + return TEST_OK; } - -/* Tests if PartitionEntryArrayCRC32 is checked. - * PartitionEntryArrayCRC32 must be calculated over SizeOfPartitionEntry * - * NumberOfPartitionEntries bytes. +/* + * Test if PartitionEntryArrayCRC32 is checked. PartitionEntryArrayCRC32 must + * be calculated over SizeOfPartitionEntry * NumberOfPartitionEntries bytes. */ -static int EntriesCrcTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - GptEntry* e1 = (GptEntry*)(gpt->primary_entries); - GptEntry* e2 = (GptEntry*)(gpt->secondary_entries); - - /* Modify the first byte of primary entries, and expect the CRC is wrong. */ - BuildTestGptData(gpt); - EXPECT(0 == CheckEntries(e1, h1)); - EXPECT(0 == CheckEntries(e2, h1)); - gpt->primary_entries[0] ^= 0xa5; /* just XOR a non-zero value */ - gpt->secondary_entries[TOTAL_ENTRIES_SIZE-1] ^= 0x5a; - EXPECT(GPT_ERROR_CRC_CORRUPTED == CheckEntries(e1, h1)); - EXPECT(GPT_ERROR_CRC_CORRUPTED == CheckEntries(e2, h1)); - - return TEST_OK; +static int EntriesCrcTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptEntry *e1 = (GptEntry *)(gpt->primary_entries); + GptEntry *e2 = (GptEntry *)(gpt->secondary_entries); + + /* Modify first byte of primary entries, and expect the CRC is wrong. */ + BuildTestGptData(gpt); + EXPECT(0 == CheckEntries(e1, h1)); + EXPECT(0 == CheckEntries(e2, h1)); + gpt->primary_entries[0] ^= 0xa5; /* just XOR a non-zero value */ + gpt->secondary_entries[TOTAL_ENTRIES_SIZE-1] ^= 0x5a; + EXPECT(GPT_ERROR_CRC_CORRUPTED == CheckEntries(e1, h1)); + EXPECT(GPT_ERROR_CRC_CORRUPTED == CheckEntries(e2, h1)); + + return TEST_OK; } - -/* Tests if partition geometry is checked. +/* + * Test if partition geometry is checked. * All active (non-zero PartitionTypeGUID) partition entries should have: * entry.StartingLBA >= header.FirstUsableLBA * entry.EndingLBA <= header.LastUsableLBA * entry.StartingLBA <= entry.EndingLBA */ -static int ValidEntryTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - GptEntry* e1 = (GptEntry*)(gpt->primary_entries); - - /* error case: entry.StartingLBA < header.FirstUsableLBA */ - BuildTestGptData(gpt); - e1[0].starting_lba = h1->first_usable_lba - 1; - RefreshCrc32(gpt); - EXPECT(GPT_ERROR_OUT_OF_REGION == CheckEntries(e1, h1)); - - /* error case: entry.EndingLBA > header.LastUsableLBA */ - BuildTestGptData(gpt); - e1[2].ending_lba = h1->last_usable_lba + 1; - RefreshCrc32(gpt); - EXPECT(GPT_ERROR_OUT_OF_REGION == CheckEntries(e1, h1)); - - /* error case: entry.StartingLBA > entry.EndingLBA */ - BuildTestGptData(gpt); - e1[3].starting_lba = e1[3].ending_lba + 1; - RefreshCrc32(gpt); - EXPECT(GPT_ERROR_OUT_OF_REGION == CheckEntries(e1, h1)); - - /* case: non active entry should be ignored. */ - BuildTestGptData(gpt); - Memset(&e1[1].type, 0, sizeof(e1[1].type)); - e1[1].starting_lba = e1[1].ending_lba + 1; - RefreshCrc32(gpt); - EXPECT(0 == CheckEntries(e1, h1)); - - return TEST_OK; +static int ValidEntryTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptEntry *e1 = (GptEntry *)(gpt->primary_entries); + + /* error case: entry.StartingLBA < header.FirstUsableLBA */ + BuildTestGptData(gpt); + e1[0].starting_lba = h1->first_usable_lba - 1; + RefreshCrc32(gpt); + EXPECT(GPT_ERROR_OUT_OF_REGION == CheckEntries(e1, h1)); + + /* error case: entry.EndingLBA > header.LastUsableLBA */ + BuildTestGptData(gpt); + e1[2].ending_lba = h1->last_usable_lba + 1; + RefreshCrc32(gpt); + EXPECT(GPT_ERROR_OUT_OF_REGION == CheckEntries(e1, h1)); + + /* error case: entry.StartingLBA > entry.EndingLBA */ + BuildTestGptData(gpt); + e1[3].starting_lba = e1[3].ending_lba + 1; + RefreshCrc32(gpt); + EXPECT(GPT_ERROR_OUT_OF_REGION == CheckEntries(e1, h1)); + + /* case: non active entry should be ignored. */ + BuildTestGptData(gpt); + Memset(&e1[1].type, 0, sizeof(e1[1].type)); + e1[1].starting_lba = e1[1].ending_lba + 1; + RefreshCrc32(gpt); + EXPECT(0 == CheckEntries(e1, h1)); + + return TEST_OK; } - -/* Tests if overlapped partition tables can be detected. */ -static int OverlappedPartitionTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h = (GptHeader*)gpt->primary_header; - GptEntry* e = (GptEntry*)gpt->primary_entries; - int i, j; - - struct { - int overlapped; - struct { - int active; - uint64_t starting_lba; - uint64_t ending_lba; - } entries[16]; /* enough for testing. */ - } cases[] = { - {GPT_SUCCESS, {{0, 100, 199}}}, - {GPT_SUCCESS, {{1, 100, 199}}}, - {GPT_SUCCESS, {{1, 100, 150}, {1, 200, 250}, {1, 300, 350}}}, - {GPT_ERROR_START_LBA_OVERLAP, - {{1, 200, 299}, {1, 100, 199}, {1, 100, 100}}}, - {GPT_ERROR_END_LBA_OVERLAP, {{1, 200, 299}, {1, 100, 199}, {1, 299, 299}}}, - {GPT_SUCCESS, {{1, 300, 399}, {1, 200, 299}, {1, 100, 199}}}, - {GPT_ERROR_END_LBA_OVERLAP, {{1, 100, 199}, {1, 199, 299}, {1, 299, 399}}}, - {GPT_ERROR_START_LBA_OVERLAP, {{1, 100, 199}, {1, 200, 299}, {1, 75, 399}}}, - {GPT_ERROR_START_LBA_OVERLAP, {{1, 100, 199}, {1, 75, 250}, {1, 200, 299}}}, - {GPT_ERROR_END_LBA_OVERLAP, {{1, 75, 150}, {1, 100, 199}, {1, 200, 299}}}, - {GPT_ERROR_START_LBA_OVERLAP, - {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {1, 100, 399}}}, - {GPT_SUCCESS, {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {0, 100, 399}}}, - {GPT_ERROR_START_LBA_OVERLAP, - {{1, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}}}, - {GPT_ERROR_START_LBA_OVERLAP, - {{0, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}}}, - {GPT_SUCCESS, {{1, 200, 300}, {1, 100, 199}, {0, 100, 400}, {0, 300, 400}}}, - {GPT_ERROR_END_LBA_OVERLAP, - {{1, 200, 299}, {1, 100, 199}, {1, 199, 199}}}, - {GPT_SUCCESS, {{1, 200, 299}, {0, 100, 199}, {1, 199, 199}}}, - {GPT_SUCCESS, {{1, 200, 299}, {1, 100, 199}, {0, 199, 199}}}, - {GPT_ERROR_START_LBA_OVERLAP, - {{1, 199, 199}, {1, 200, 200}, {1, 201, 201}, {1, 202, 202}, - {1, 203, 203}, {1, 204, 204}, {1, 205, 205}, {1, 206, 206}, - {1, 207, 207}, {1, 208, 208}, {1, 199, 199}}}, - {GPT_SUCCESS, {{1, 199, 199}, {1, 200, 200}, {1, 201, 201}, {1, 202, 202}, - {1, 203, 203}, {1, 204, 204}, {1, 205, 205}, {1, 206, 206}, - {1, 207, 207}, {1, 208, 208}, {0, 199, 199}}}, - }; - - - for (i = 0; i < ARRAY_SIZE(cases); ++i) { - BuildTestGptData(gpt); - ZeroEntries(gpt); - for(j = 0; j < ARRAY_SIZE(cases[0].entries); ++j) { - if (!cases[i].entries[j].starting_lba) - break; - - if (cases[i].entries[j].active) - Memcpy(&e[j].type, &guid_kernel, sizeof(Guid)); - SetGuid(&e[j].unique, j); - e[j].starting_lba = cases[i].entries[j].starting_lba; - e[j].ending_lba = cases[i].entries[j].ending_lba; - } - RefreshCrc32(gpt); - - EXPECT(cases[i].overlapped == CheckEntries(e, h)); - } - return TEST_OK; +/* Test if overlapped partition tables can be detected. */ +static int OverlappedPartitionTest(void) { + GptData *gpt = GetEmptyGptData(); + GptHeader *h = (GptHeader *)gpt->primary_header; + GptEntry *e = (GptEntry *)gpt->primary_entries; + int i, j; + + struct { + int overlapped; + struct { + int active; + uint64_t starting_lba; + uint64_t ending_lba; + } entries[16]; /* enough for testing. */ + } cases[] = { + {GPT_SUCCESS, {{0, 100, 199}}}, + {GPT_SUCCESS, {{1, 100, 199}}}, + {GPT_SUCCESS, {{1, 100, 150}, {1, 200, 250}, {1, 300, 350}}}, + {GPT_ERROR_START_LBA_OVERLAP, + {{1, 200, 299}, {1, 100, 199}, {1, 100, 100}}}, + {GPT_ERROR_END_LBA_OVERLAP, + {{1, 200, 299}, {1, 100, 199}, {1, 299, 299}}}, + {GPT_SUCCESS, {{1, 300, 399}, {1, 200, 299}, {1, 100, 199}}}, + {GPT_ERROR_END_LBA_OVERLAP, + {{1, 100, 199}, {1, 199, 299}, {1, 299, 399}}}, + {GPT_ERROR_START_LBA_OVERLAP, + {{1, 100, 199}, {1, 200, 299}, {1, 75, 399}}}, + {GPT_ERROR_START_LBA_OVERLAP, + {{1, 100, 199}, {1, 75, 250}, {1, 200, 299}}}, + {GPT_ERROR_END_LBA_OVERLAP, + {{1, 75, 150}, {1, 100, 199}, {1, 200, 299}}}, + {GPT_ERROR_START_LBA_OVERLAP, + {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {1, 100, 399}}}, + {GPT_SUCCESS, + {{1, 200, 299}, {1, 100, 199}, {1, 300, 399}, {0, 100, 399}}}, + {GPT_ERROR_START_LBA_OVERLAP, + {{1, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}}}, + {GPT_ERROR_START_LBA_OVERLAP, + {{0, 200, 300}, {1, 100, 200}, {1, 100, 400}, {1, 300, 400}}}, + {GPT_SUCCESS, + {{1, 200, 300}, {1, 100, 199}, {0, 100, 400}, {0, 300, 400}}}, + {GPT_ERROR_END_LBA_OVERLAP, + {{1, 200, 299}, {1, 100, 199}, {1, 199, 199}}}, + {GPT_SUCCESS, {{1, 200, 299}, {0, 100, 199}, {1, 199, 199}}}, + {GPT_SUCCESS, {{1, 200, 299}, {1, 100, 199}, {0, 199, 199}}}, + {GPT_ERROR_START_LBA_OVERLAP, + {{1, 199, 199}, {1, 200, 200}, {1, 201, 201}, {1, 202, 202}, + {1, 203, 203}, {1, 204, 204}, {1, 205, 205}, {1, 206, 206}, + {1, 207, 207}, {1, 208, 208}, {1, 199, 199}}}, + {GPT_SUCCESS, + {{1, 199, 199}, {1, 200, 200}, {1, 201, 201}, {1, 202, 202}, + {1, 203, 203}, {1, 204, 204}, {1, 205, 205}, {1, 206, 206}, + {1, 207, 207}, {1, 208, 208}, {0, 199, 199}}}, + }; + + for (i = 0; i < ARRAY_SIZE(cases); ++i) { + BuildTestGptData(gpt); + ZeroEntries(gpt); + for(j = 0; j < ARRAY_SIZE(cases[0].entries); ++j) { + if (!cases[i].entries[j].starting_lba) + break; + + if (cases[i].entries[j].active) + Memcpy(&e[j].type, &guid_kernel, sizeof(Guid)); + SetGuid(&e[j].unique, j); + e[j].starting_lba = cases[i].entries[j].starting_lba; + e[j].ending_lba = cases[i].entries[j].ending_lba; + } + RefreshCrc32(gpt); + + EXPECT(cases[i].overlapped == CheckEntries(e, h)); + } + return TEST_OK; } - /* Test both sanity checking and repair. */ -static int SanityCheckTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h1 = (GptHeader*)gpt->primary_header; - - /* Unmodified test data is completely sane */ - BuildTestGptData(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - /* Repair doesn't damage it */ - GptRepair(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - EXPECT(0 == gpt->modified); - - /* Modify headers */ - BuildTestGptData(gpt); - gpt->primary_header[0]++; - gpt->secondary_header[0]++; - EXPECT(GPT_ERROR_INVALID_HEADERS == GptSanityCheck(gpt)); - EXPECT(0 == gpt->valid_headers); - EXPECT(0 == gpt->valid_entries); - /* Repair can't fix completely busted headers */ - GptRepair(gpt); - EXPECT(GPT_ERROR_INVALID_HEADERS == GptSanityCheck(gpt)); - EXPECT(0 == gpt->valid_headers); - EXPECT(0 == gpt->valid_entries); - EXPECT(0 == gpt->modified); - - BuildTestGptData(gpt); - gpt->primary_header[0]++; - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_SECONDARY == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - GptRepair(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - EXPECT(GPT_MODIFIED_HEADER1 == gpt->modified); - - BuildTestGptData(gpt); - gpt->secondary_header[0]++; - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_PRIMARY == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - GptRepair(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified); - - /* Modify header1 and update its CRC. Since header2 is now different than - * header1, it'll be the one considered invalid. */ - BuildTestGptData(gpt); - h1->size++; - RefreshCrc32(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_PRIMARY == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - GptRepair(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified); - - /* Modify entries */ - BuildTestGptData(gpt); - gpt->primary_entries[0]++; - gpt->secondary_entries[0]++; - EXPECT(GPT_ERROR_INVALID_ENTRIES == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_NONE == gpt->valid_entries); - /* Repair can't fix both copies of entries being bad, either. */ - GptRepair(gpt); - EXPECT(GPT_ERROR_INVALID_ENTRIES == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_NONE == gpt->valid_entries); - EXPECT(0 == gpt->modified); - - BuildTestGptData(gpt); - gpt->primary_entries[0]++; - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_SECONDARY == gpt->valid_entries); - GptRepair(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - EXPECT(GPT_MODIFIED_ENTRIES1 == gpt->modified); - - BuildTestGptData(gpt); - gpt->secondary_entries[0]++; - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_PRIMARY == gpt->valid_entries); - GptRepair(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - EXPECT(GPT_MODIFIED_ENTRIES2 == gpt->modified); - - /* Modify both header and entries */ - BuildTestGptData(gpt); - gpt->primary_header[0]++; - gpt->primary_entries[0]++; - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_SECONDARY == gpt->valid_headers); - EXPECT(MASK_SECONDARY == gpt->valid_entries); - GptRepair(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1) == gpt->modified); - - BuildTestGptData(gpt); - gpt->secondary_header[0]++; - gpt->secondary_entries[0]++; - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_PRIMARY == gpt->valid_headers); - EXPECT(MASK_PRIMARY == gpt->valid_entries); - GptRepair(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified); - - /* Test cross-correction (h1+e2, h2+e1) */ - BuildTestGptData(gpt); - gpt->primary_header[0]++; - gpt->secondary_entries[0]++; - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_SECONDARY == gpt->valid_headers); - EXPECT(MASK_PRIMARY == gpt->valid_entries); - GptRepair(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES2) == gpt->modified); - - BuildTestGptData(gpt); - gpt->secondary_header[0]++; - gpt->primary_entries[0]++; - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_PRIMARY == gpt->valid_headers); - EXPECT(MASK_SECONDARY == gpt->valid_entries); - GptRepair(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES1) == gpt->modified); - - /* Test mismatched pairs (h1+e1 valid, h2+e2 valid but different. - * This simulates a partial update of the drive. */ - BuildTestGptData(gpt); - gpt->secondary_entries[0]++; - RefreshCrc32(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_PRIMARY == gpt->valid_headers); - EXPECT(MASK_PRIMARY == gpt->valid_entries); - GptRepair(gpt); - EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); - EXPECT(MASK_BOTH == gpt->valid_headers); - EXPECT(MASK_BOTH == gpt->valid_entries); - EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified); - - return TEST_OK; +static int SanityCheckTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h1 = (GptHeader *)gpt->primary_header; + GptEntry *e1 = (GptEntry *)gpt->primary_entries; + uint8_t *tempptr; + + /* Unmodified test data is completely sane */ + BuildTestGptData(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + /* Repair doesn't damage it */ + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(0 == gpt->modified); + + /* Invalid sector size should fail */ + BuildTestGptData(gpt); + gpt->sector_bytes = 1024; + EXPECT(GPT_ERROR_INVALID_SECTOR_SIZE == GptSanityCheck(gpt)); + + /* Modify headers */ + BuildTestGptData(gpt); + gpt->primary_header[0]++; + gpt->secondary_header[0]++; + EXPECT(GPT_ERROR_INVALID_HEADERS == GptSanityCheck(gpt)); + EXPECT(0 == gpt->valid_headers); + EXPECT(0 == gpt->valid_entries); + /* Repair can't fix completely busted headers */ + GptRepair(gpt); + EXPECT(GPT_ERROR_INVALID_HEADERS == GptSanityCheck(gpt)); + EXPECT(0 == gpt->valid_headers); + EXPECT(0 == gpt->valid_entries); + EXPECT(0 == gpt->modified); + + BuildTestGptData(gpt); + gpt->primary_header[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_SECONDARY == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(GPT_MODIFIED_HEADER1 == gpt->modified); + + BuildTestGptData(gpt); + gpt->secondary_header[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_PRIMARY == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified); + + /* + * Modify header1 and update its CRC. Since header2 is now different + * than header1, it'll be the one considered invalid. + */ + BuildTestGptData(gpt); + h1->size++; + RefreshCrc32(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_PRIMARY == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(GPT_MODIFIED_HEADER2 == gpt->modified); + + /* Modify entries */ + BuildTestGptData(gpt); + gpt->primary_entries[0]++; + gpt->secondary_entries[0]++; + EXPECT(GPT_ERROR_INVALID_ENTRIES == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_NONE == gpt->valid_entries); + /* Repair can't fix both copies of entries being bad, either. */ + GptRepair(gpt); + EXPECT(GPT_ERROR_INVALID_ENTRIES == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_NONE == gpt->valid_entries); + EXPECT(0 == gpt->modified); + + BuildTestGptData(gpt); + gpt->primary_entries[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_SECONDARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(GPT_MODIFIED_ENTRIES1 == gpt->modified); + + BuildTestGptData(gpt); + gpt->secondary_entries[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_PRIMARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT(GPT_MODIFIED_ENTRIES2 == gpt->modified); + + /* + * Modify entries and recompute CRCs, then make both primary and + * secondary entry pointers use the secondary data. The primary + * header will have the wrong entries CRC, so we should fall back + * to the secondary header. + */ + BuildTestGptData(gpt); + e1->starting_lba++; + RefreshCrc32(gpt); + tempptr = gpt->primary_entries; + gpt->primary_entries = gpt->secondary_entries; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_SECONDARY == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + gpt->primary_entries = tempptr; + + /* Modify both header and entries */ + BuildTestGptData(gpt); + gpt->primary_header[0]++; + gpt->primary_entries[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_SECONDARY == gpt->valid_headers); + EXPECT(MASK_SECONDARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1) == gpt->modified); + + BuildTestGptData(gpt); + gpt->secondary_header[0]++; + gpt->secondary_entries[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_PRIMARY == gpt->valid_headers); + EXPECT(MASK_PRIMARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified); + + /* Test cross-correction (h1+e2, h2+e1) */ + BuildTestGptData(gpt); + gpt->primary_header[0]++; + gpt->secondary_entries[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_SECONDARY == gpt->valid_headers); + EXPECT(MASK_PRIMARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT((GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES2) == gpt->modified); + + BuildTestGptData(gpt); + gpt->secondary_header[0]++; + gpt->primary_entries[0]++; + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_PRIMARY == gpt->valid_headers); + EXPECT(MASK_SECONDARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES1) == gpt->modified); + + /* + * Test mismatched pairs (h1+e1 valid, h2+e2 valid but different. This + * simulates a partial update of the drive. + */ + BuildTestGptData(gpt); + gpt->secondary_entries[0]++; + RefreshCrc32(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_PRIMARY == gpt->valid_headers); + EXPECT(MASK_PRIMARY == gpt->valid_entries); + GptRepair(gpt); + EXPECT(GPT_SUCCESS == GptSanityCheck(gpt)); + EXPECT(MASK_BOTH == gpt->valid_headers); + EXPECT(MASK_BOTH == gpt->valid_entries); + EXPECT((GPT_MODIFIED_HEADER2 | GPT_MODIFIED_ENTRIES2) == gpt->modified); + + return TEST_OK; } - -static int EntryAttributeGetSetTest() { - GptData* gpt = GetEmptyGptData(); - GptEntry* e = (GptEntry*)(gpt->primary_entries); - - e->attrs.whole = 0x0000000000000000ULL; - SetEntrySuccessful(e, 1); - EXPECT(0x0100000000000000ULL == e->attrs.whole); - EXPECT(1 == GetEntrySuccessful(e)); - e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; - SetEntrySuccessful(e, 0); - EXPECT(0xFEFFFFFFFFFFFFFFULL == e->attrs.whole); - EXPECT(0 == GetEntrySuccessful(e)); - - e->attrs.whole = 0x0000000000000000ULL; - SetEntryTries(e, 15); - EXPECT(15 == GetEntryTries(e)); - EXPECT(0x00F0000000000000ULL == e->attrs.whole); - e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; - SetEntryTries(e, 0); - EXPECT(0xFF0FFFFFFFFFFFFFULL == e->attrs.whole); - EXPECT(0 == GetEntryTries(e)); - - e->attrs.whole = 0x0000000000000000ULL; - SetEntryPriority(e, 15); - EXPECT(0x000F000000000000ULL == e->attrs.whole); - EXPECT(15 == GetEntryPriority(e)); - e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; - SetEntryPriority(e, 0); - EXPECT(0xFFF0FFFFFFFFFFFFULL == e->attrs.whole); - EXPECT(0 == GetEntryPriority(e)); - - e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; - EXPECT(1 == GetEntrySuccessful(e)); - EXPECT(15 == GetEntryPriority(e)); - EXPECT(15 == GetEntryTries(e)); - - e->attrs.whole = 0x0123000000000000ULL; - EXPECT(1 == GetEntrySuccessful(e)); - EXPECT(2 == GetEntryTries(e)); - EXPECT(3 == GetEntryPriority(e)); - - return TEST_OK; +static int EntryAttributeGetSetTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptEntry *e = (GptEntry *)(gpt->primary_entries); + + e->attrs.whole = 0x0000000000000000ULL; + SetEntrySuccessful(e, 1); + EXPECT(0x0100000000000000ULL == e->attrs.whole); + EXPECT(1 == GetEntrySuccessful(e)); + e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; + SetEntrySuccessful(e, 0); + EXPECT(0xFEFFFFFFFFFFFFFFULL == e->attrs.whole); + EXPECT(0 == GetEntrySuccessful(e)); + + e->attrs.whole = 0x0000000000000000ULL; + SetEntryTries(e, 15); + EXPECT(15 == GetEntryTries(e)); + EXPECT(0x00F0000000000000ULL == e->attrs.whole); + e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; + SetEntryTries(e, 0); + EXPECT(0xFF0FFFFFFFFFFFFFULL == e->attrs.whole); + EXPECT(0 == GetEntryTries(e)); + + e->attrs.whole = 0x0000000000000000ULL; + SetEntryPriority(e, 15); + EXPECT(0x000F000000000000ULL == e->attrs.whole); + EXPECT(15 == GetEntryPriority(e)); + e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; + SetEntryPriority(e, 0); + EXPECT(0xFFF0FFFFFFFFFFFFULL == e->attrs.whole); + EXPECT(0 == GetEntryPriority(e)); + + e->attrs.whole = 0xFFFFFFFFFFFFFFFFULL; + EXPECT(1 == GetEntrySuccessful(e)); + EXPECT(15 == GetEntryPriority(e)); + EXPECT(15 == GetEntryTries(e)); + + e->attrs.whole = 0x0123000000000000ULL; + EXPECT(1 == GetEntrySuccessful(e)); + EXPECT(2 == GetEntryTries(e)); + EXPECT(3 == GetEntryPriority(e)); + + return TEST_OK; } +static int EntryTypeTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptEntry *e = (GptEntry *)(gpt->primary_entries); -static int EntryTypeTest() { - GptData* gpt = GetEmptyGptData(); - GptEntry* e = (GptEntry*)(gpt->primary_entries); - - Memcpy(&e->type, &guid_zero, sizeof(Guid)); - EXPECT(1 == IsUnusedEntry(e)); - EXPECT(0 == IsKernelEntry(e)); + Memcpy(&e->type, &guid_zero, sizeof(Guid)); + EXPECT(1 == IsUnusedEntry(e)); + EXPECT(0 == IsKernelEntry(e)); - Memcpy(&e->type, &guid_kernel, sizeof(Guid)); - EXPECT(0 == IsUnusedEntry(e)); - EXPECT(1 == IsKernelEntry(e)); + Memcpy(&e->type, &guid_kernel, sizeof(Guid)); + EXPECT(0 == IsUnusedEntry(e)); + EXPECT(1 == IsKernelEntry(e)); - Memcpy(&e->type, &guid_rootfs, sizeof(Guid)); - EXPECT(0 == IsUnusedEntry(e)); - EXPECT(0 == IsKernelEntry(e)); + Memcpy(&e->type, &guid_rootfs, sizeof(Guid)); + EXPECT(0 == IsUnusedEntry(e)); + EXPECT(0 == IsKernelEntry(e)); - return TEST_OK; + return TEST_OK; } - /* Make an entry unused by clearing its type. */ -static void FreeEntry(GptEntry* e) { - Memset(&e->type, 0, sizeof(Guid)); +static void FreeEntry(GptEntry *e) +{ + Memset(&e->type, 0, sizeof(Guid)); } - /* Set up an entry. */ -static void FillEntry(GptEntry* e, int is_kernel, - int priority, int successful, int tries) { - Memcpy(&e->type, (is_kernel ? &guid_kernel : &guid_zero), sizeof(Guid)); - SetEntryPriority(e, priority); - SetEntrySuccessful(e, successful); - SetEntryTries(e, tries); +static void FillEntry(GptEntry *e, int is_kernel, + int priority, int successful, int tries) +{ + Memcpy(&e->type, (is_kernel ? &guid_kernel : &guid_zero), sizeof(Guid)); + SetEntryPriority(e, priority); + SetEntrySuccessful(e, successful); + SetEntryTries(e, tries); } - -/* Invalidate all kernel entries and expect GptNextKernelEntry() cannot find +/* + * Invalidate all kernel entries and expect GptNextKernelEntry() cannot find * any usable kernel entry. */ -static int NoValidKernelEntryTest() { - GptData* gpt = GetEmptyGptData(); - GptEntry* e1 = (GptEntry*)(gpt->primary_entries); - - BuildTestGptData(gpt); - SetEntryPriority(e1 + KERNEL_A, 0); - FreeEntry(e1 + KERNEL_B); - RefreshCrc32(gpt); - EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, NULL, NULL)); - - return TEST_OK; +static int NoValidKernelEntryTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptEntry *e1 = (GptEntry *)(gpt->primary_entries); + + BuildTestGptData(gpt); + SetEntryPriority(e1 + KERNEL_A, 0); + FreeEntry(e1 + KERNEL_B); + RefreshCrc32(gpt); + EXPECT(GPT_ERROR_NO_VALID_KERNEL == + GptNextKernelEntry(gpt, NULL, NULL)); + + return TEST_OK; } - -static int GetNextNormalTest() { - GptData* gpt = GetEmptyGptData(); - GptEntry* e1 = (GptEntry*)(gpt->primary_entries); - uint64_t start, size; - - /* Normal case - both kernels successful */ - BuildTestGptData(gpt); - FillEntry(e1 + KERNEL_A, 1, 2, 1, 0); - FillEntry(e1 + KERNEL_B, 1, 2, 1, 0); - RefreshCrc32(gpt); - GptInit(gpt); - - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(KERNEL_A == gpt->current_kernel); - EXPECT(34 == start); - EXPECT(100 == size); - - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(KERNEL_B == gpt->current_kernel); - EXPECT(134 == start); - EXPECT(99 == size); - - EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(-1 == gpt->current_kernel); - - /* Call as many times as you want; you won't get another kernel... */ - EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(-1 == gpt->current_kernel); - - return TEST_OK; +static int GetNextNormalTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptEntry *e1 = (GptEntry *)(gpt->primary_entries); + uint64_t start, size; + + /* Normal case - both kernels successful */ + BuildTestGptData(gpt); + FillEntry(e1 + KERNEL_A, 1, 2, 1, 0); + FillEntry(e1 + KERNEL_B, 1, 2, 1, 0); + RefreshCrc32(gpt); + GptInit(gpt); + + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_A == gpt->current_kernel); + EXPECT(34 == start); + EXPECT(100 == size); + + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_B == gpt->current_kernel); + EXPECT(134 == start); + EXPECT(99 == size); + + EXPECT(GPT_ERROR_NO_VALID_KERNEL == + GptNextKernelEntry(gpt, &start, &size)); + EXPECT(-1 == gpt->current_kernel); + + /* Call as many times as you want; you won't get another kernel... */ + EXPECT(GPT_ERROR_NO_VALID_KERNEL == + GptNextKernelEntry(gpt, &start, &size)); + EXPECT(-1 == gpt->current_kernel); + + return TEST_OK; } - -static int GetNextPrioTest() { - GptData* gpt = GetEmptyGptData(); - GptEntry* e1 = (GptEntry*)(gpt->primary_entries); - uint64_t start, size; - - /* Priority 3, 4, 0, 4 - should boot order B, Y, A */ - BuildTestGptData(gpt); - FillEntry(e1 + KERNEL_A, 1, 3, 1, 0); - FillEntry(e1 + KERNEL_B, 1, 4, 1, 0); - FillEntry(e1 + KERNEL_X, 1, 0, 1, 0); - FillEntry(e1 + KERNEL_Y, 1, 4, 1, 0); - RefreshCrc32(gpt); - GptInit(gpt); - - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(KERNEL_B == gpt->current_kernel); - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(KERNEL_Y == gpt->current_kernel); - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(KERNEL_A == gpt->current_kernel); - EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, &start, &size)); - - return TEST_OK; +static int GetNextPrioTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptEntry *e1 = (GptEntry *)(gpt->primary_entries); + uint64_t start, size; + + /* Priority 3, 4, 0, 4 - should boot order B, Y, A */ + BuildTestGptData(gpt); + FillEntry(e1 + KERNEL_A, 1, 3, 1, 0); + FillEntry(e1 + KERNEL_B, 1, 4, 1, 0); + FillEntry(e1 + KERNEL_X, 1, 0, 1, 0); + FillEntry(e1 + KERNEL_Y, 1, 4, 1, 0); + RefreshCrc32(gpt); + GptInit(gpt); + + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_B == gpt->current_kernel); + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_Y == gpt->current_kernel); + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_A == gpt->current_kernel); + EXPECT(GPT_ERROR_NO_VALID_KERNEL == + GptNextKernelEntry(gpt, &start, &size)); + + return TEST_OK; } - -static int GetNextTriesTest() { - GptData* gpt = GetEmptyGptData(); - GptEntry* e1 = (GptEntry*)(gpt->primary_entries); - uint64_t start, size; - - /* Tries=nonzero is attempted just like success, but tries=0 isn't */ - BuildTestGptData(gpt); - FillEntry(e1 + KERNEL_A, 1, 2, 1, 0); - FillEntry(e1 + KERNEL_B, 1, 3, 0, 0); - FillEntry(e1 + KERNEL_X, 1, 4, 0, 1); - FillEntry(e1 + KERNEL_Y, 1, 0, 0, 5); - RefreshCrc32(gpt); - GptInit(gpt); - - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(KERNEL_X == gpt->current_kernel); - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(KERNEL_A == gpt->current_kernel); - EXPECT(GPT_ERROR_NO_VALID_KERNEL == GptNextKernelEntry(gpt, &start, &size)); - - return TEST_OK; +static int GetNextTriesTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptEntry *e1 = (GptEntry *)(gpt->primary_entries); + uint64_t start, size; + + /* Tries=nonzero is attempted just like success, but tries=0 isn't */ + BuildTestGptData(gpt); + FillEntry(e1 + KERNEL_A, 1, 2, 1, 0); + FillEntry(e1 + KERNEL_B, 1, 3, 0, 0); + FillEntry(e1 + KERNEL_X, 1, 4, 0, 1); + FillEntry(e1 + KERNEL_Y, 1, 0, 0, 5); + RefreshCrc32(gpt); + GptInit(gpt); + + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_X == gpt->current_kernel); + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_A == gpt->current_kernel); + EXPECT(GPT_ERROR_NO_VALID_KERNEL == + GptNextKernelEntry(gpt, &start, &size)); + + return TEST_OK; } - -static int GptUpdateTest() { - GptData* gpt = GetEmptyGptData(); - GptEntry* e = (GptEntry*)(gpt->primary_entries); - GptEntry* e2 = (GptEntry*)(gpt->secondary_entries); - uint64_t start, size; - - /* Tries=nonzero is attempted just like success, but tries=0 isn't */ - BuildTestGptData(gpt); - FillEntry(e + KERNEL_A, 1, 4, 1, 0); - FillEntry(e + KERNEL_B, 1, 3, 0, 2); - FillEntry(e + KERNEL_X, 1, 2, 0, 2); - RefreshCrc32(gpt); - GptInit(gpt); - gpt->modified = 0; /* Nothing modified yet */ - - /* Successful kernel */ - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(KERNEL_A == gpt->current_kernel); - EXPECT(1 == GetEntrySuccessful(e + KERNEL_A)); - EXPECT(4 == GetEntryPriority(e + KERNEL_A)); - EXPECT(0 == GetEntryTries(e + KERNEL_A)); - EXPECT(1 == GetEntrySuccessful(e2 + KERNEL_A)); - EXPECT(4 == GetEntryPriority(e2 + KERNEL_A)); - EXPECT(0 == GetEntryTries(e2 + KERNEL_A)); - /* Trying successful kernel changes nothing */ - EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); - EXPECT(1 == GetEntrySuccessful(e + KERNEL_A)); - EXPECT(4 == GetEntryPriority(e + KERNEL_A)); - EXPECT(0 == GetEntryTries(e + KERNEL_A)); - EXPECT(0 == gpt->modified); - /* Marking it bad also does not update it. */ - EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); - EXPECT(1 == GetEntrySuccessful(e + KERNEL_A)); - EXPECT(4 == GetEntryPriority(e + KERNEL_A)); - EXPECT(0 == GetEntryTries(e + KERNEL_A)); - EXPECT(0 == gpt->modified); - - /* Kernel with tries */ - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(KERNEL_B == gpt->current_kernel); - EXPECT(0 == GetEntrySuccessful(e + KERNEL_B)); - EXPECT(3 == GetEntryPriority(e + KERNEL_B)); - EXPECT(2 == GetEntryTries(e + KERNEL_B)); - /* Marking it bad clears it */ - EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); - EXPECT(0 == GetEntrySuccessful(e + KERNEL_B)); - EXPECT(0 == GetEntryPriority(e + KERNEL_B)); - EXPECT(0 == GetEntryTries(e + KERNEL_B)); - /* Which affects both copies of the partition entries */ - EXPECT(0 == GetEntrySuccessful(e2 + KERNEL_B)); - EXPECT(0 == GetEntryPriority(e2 + KERNEL_B)); - EXPECT(0 == GetEntryTries(e2 + KERNEL_B)); - /* And that's caused the GPT to need updating */ - EXPECT(0x0F == gpt->modified); - - /* Another kernel with tries */ - EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); - EXPECT(KERNEL_X == gpt->current_kernel); - EXPECT(0 == GetEntrySuccessful(e + KERNEL_X)); - EXPECT(2 == GetEntryPriority(e + KERNEL_X)); - EXPECT(2 == GetEntryTries(e + KERNEL_X)); - /* Trying it uses up a try */ - EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); - EXPECT(0 == GetEntrySuccessful(e + KERNEL_X)); - EXPECT(2 == GetEntryPriority(e + KERNEL_X)); - EXPECT(1 == GetEntryTries(e + KERNEL_X)); - EXPECT(0 == GetEntrySuccessful(e2 + KERNEL_X)); - EXPECT(2 == GetEntryPriority(e2 + KERNEL_X)); - EXPECT(1 == GetEntryTries(e2 + KERNEL_X)); - /* Trying it again marks it inactive */ - EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); - EXPECT(0 == GetEntrySuccessful(e + KERNEL_X)); - EXPECT(0 == GetEntryPriority(e + KERNEL_X)); - EXPECT(0 == GetEntryTries(e + KERNEL_X)); - - return TEST_OK; +static int GptUpdateTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptEntry *e = (GptEntry *)(gpt->primary_entries); + GptEntry *e2 = (GptEntry *)(gpt->secondary_entries); + uint64_t start, size; + + /* Tries=nonzero is attempted just like success, but tries=0 isn't */ + BuildTestGptData(gpt); + FillEntry(e + KERNEL_A, 1, 4, 1, 0); + FillEntry(e + KERNEL_B, 1, 3, 0, 2); + FillEntry(e + KERNEL_X, 1, 2, 0, 2); + RefreshCrc32(gpt); + GptInit(gpt); + gpt->modified = 0; /* Nothing modified yet */ + + /* Successful kernel */ + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_A == gpt->current_kernel); + EXPECT(1 == GetEntrySuccessful(e + KERNEL_A)); + EXPECT(4 == GetEntryPriority(e + KERNEL_A)); + EXPECT(0 == GetEntryTries(e + KERNEL_A)); + EXPECT(1 == GetEntrySuccessful(e2 + KERNEL_A)); + EXPECT(4 == GetEntryPriority(e2 + KERNEL_A)); + EXPECT(0 == GetEntryTries(e2 + KERNEL_A)); + /* Trying successful kernel changes nothing */ + EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); + EXPECT(1 == GetEntrySuccessful(e + KERNEL_A)); + EXPECT(4 == GetEntryPriority(e + KERNEL_A)); + EXPECT(0 == GetEntryTries(e + KERNEL_A)); + EXPECT(0 == gpt->modified); + /* Marking it bad also does not update it. */ + EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); + EXPECT(1 == GetEntrySuccessful(e + KERNEL_A)); + EXPECT(4 == GetEntryPriority(e + KERNEL_A)); + EXPECT(0 == GetEntryTries(e + KERNEL_A)); + EXPECT(0 == gpt->modified); + + /* Kernel with tries */ + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_B == gpt->current_kernel); + EXPECT(0 == GetEntrySuccessful(e + KERNEL_B)); + EXPECT(3 == GetEntryPriority(e + KERNEL_B)); + EXPECT(2 == GetEntryTries(e + KERNEL_B)); + /* Marking it bad clears it */ + EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); + EXPECT(0 == GetEntrySuccessful(e + KERNEL_B)); + EXPECT(0 == GetEntryPriority(e + KERNEL_B)); + EXPECT(0 == GetEntryTries(e + KERNEL_B)); + /* Which affects both copies of the partition entries */ + EXPECT(0 == GetEntrySuccessful(e2 + KERNEL_B)); + EXPECT(0 == GetEntryPriority(e2 + KERNEL_B)); + EXPECT(0 == GetEntryTries(e2 + KERNEL_B)); + /* And that's caused the GPT to need updating */ + EXPECT(0x0F == gpt->modified); + + /* Another kernel with tries */ + EXPECT(GPT_SUCCESS == GptNextKernelEntry(gpt, &start, &size)); + EXPECT(KERNEL_X == gpt->current_kernel); + EXPECT(0 == GetEntrySuccessful(e + KERNEL_X)); + EXPECT(2 == GetEntryPriority(e + KERNEL_X)); + EXPECT(2 == GetEntryTries(e + KERNEL_X)); + /* Trying it uses up a try */ + EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); + EXPECT(0 == GetEntrySuccessful(e + KERNEL_X)); + EXPECT(2 == GetEntryPriority(e + KERNEL_X)); + EXPECT(1 == GetEntryTries(e + KERNEL_X)); + EXPECT(0 == GetEntrySuccessful(e2 + KERNEL_X)); + EXPECT(2 == GetEntryPriority(e2 + KERNEL_X)); + EXPECT(1 == GetEntryTries(e2 + KERNEL_X)); + /* Trying it again marks it inactive */ + EXPECT(GPT_SUCCESS == GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_TRY)); + EXPECT(0 == GetEntrySuccessful(e + KERNEL_X)); + EXPECT(0 == GetEntryPriority(e + KERNEL_X)); + EXPECT(0 == GetEntryTries(e + KERNEL_X)); + + /* Can't update if entry isn't a kernel, or there isn't an entry */ + Memcpy(&e[KERNEL_X].type, &guid_rootfs, sizeof(guid_rootfs)); + EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == + GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); + gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; + EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == + GptUpdateKernelEntry(gpt, GPT_UPDATE_ENTRY_BAD)); + + + return TEST_OK; } +/* + * Give an invalid kernel type, and expect GptUpdateKernelEntry() returns + * GPT_ERROR_INVALID_UPDATE_TYPE. + */ +static int UpdateInvalidKernelTypeTest(void) +{ + GptData *gpt = GetEmptyGptData(); + + BuildTestGptData(gpt); + /* anything, but not CGPT_KERNEL_ENTRY_NOT_FOUND */ + gpt->current_kernel = 0; + /* any invalid update_type value */ + EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == + GptUpdateKernelEntry(gpt, 99)); + + return TEST_OK; +} -/* Given an invalid kernel type, and expect GptUpdateKernelEntry() returns - * GPT_ERROR_INVALID_UPDATE_TYPE. */ -static int UpdateInvalidKernelTypeTest() { - GptData* gpt = GetEmptyGptData(); - - BuildTestGptData(gpt); - gpt->current_kernel = 0; /* anything, but not CGPT_KERNEL_ENTRY_NOT_FOUND */ - EXPECT(GPT_ERROR_INVALID_UPDATE_TYPE == - GptUpdateKernelEntry(gpt, 99)); /* any invalid update_type value */ +/* Test duplicate UniqueGuids can be detected. */ +static int DuplicateUniqueGuidTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptHeader *h = (GptHeader *)gpt->primary_header; + GptEntry *e = (GptEntry *)gpt->primary_entries; + int i, j; + + struct { + int duplicate; + struct { + uint64_t starting_lba; + uint64_t ending_lba; + uint32_t type_guid; + uint32_t unique_guid; + } entries[16]; /* enough for testing. */ + } cases[] = { + {GPT_SUCCESS, {{100, 109, 1, 1}, + {110, 119, 2, 2}, + {120, 129, 3, 3}, + {130, 139, 4, 4}, + }}, + {GPT_SUCCESS, {{100, 109, 1, 1}, + {110, 119, 1, 2}, + {120, 129, 2, 3}, + {130, 139, 2, 4}, + }}, + {GPT_ERROR_DUP_GUID, {{100, 109, 1, 1}, + {110, 119, 2, 2}, + {120, 129, 3, 1}, + {130, 139, 4, 4}, + }}, + {GPT_ERROR_DUP_GUID, {{100, 109, 1, 1}, + {110, 119, 1, 2}, + {120, 129, 2, 3}, + {130, 139, 2, 2}, + }}, + }; + + for (i = 0; i < ARRAY_SIZE(cases); ++i) { + BuildTestGptData(gpt); + ZeroEntries(gpt); + for(j = 0; j < ARRAY_SIZE(cases[0].entries); ++j) { + if (!cases[i].entries[j].starting_lba) + break; + + e[j].starting_lba = cases[i].entries[j].starting_lba; + e[j].ending_lba = cases[i].entries[j].ending_lba; + SetGuid(&e[j].type, cases[i].entries[j].type_guid); + SetGuid(&e[j].unique, cases[i].entries[j].unique_guid); + } + RefreshCrc32(gpt); + + EXPECT(cases[i].duplicate == CheckEntries(e, h)); + } + + return TEST_OK; +} - return TEST_OK; +/* Test getting the current kernel GUID */ +static int GetKernelGuidTest(void) +{ + GptData *gpt = GetEmptyGptData(); + GptEntry *e = (GptEntry *)gpt->primary_entries; + Guid g; + + BuildTestGptData(gpt); + gpt->current_kernel = 0; + GetCurrentKernelUniqueGuid(gpt, &g); + EXPECT(!Memcmp(&g, &e[0].unique, sizeof(Guid))); + gpt->current_kernel = 1; + GetCurrentKernelUniqueGuid(gpt, &g); + EXPECT(!Memcmp(&g, &e[1].unique, sizeof(Guid))); + + return TEST_OK; } +/* Test getting GPT error text strings */ +static int ErrorTextTest(void) +{ + int i; -/* Tests duplicate UniqueGuids can be detected. */ -static int DuplicateUniqueGuidTest() { - GptData* gpt = GetEmptyGptData(); - GptHeader* h = (GptHeader*)gpt->primary_header; - GptEntry* e = (GptEntry*)gpt->primary_entries; - int i, j; - - struct { - int duplicate; - struct { - uint64_t starting_lba; - uint64_t ending_lba; - uint32_t type_guid; - uint32_t unique_guid; - } entries[16]; /* enough for testing. */ - } cases[] = { - {GPT_SUCCESS, {{100, 109, 1, 1}, - {110, 119, 2, 2}, - {120, 129, 3, 3}, - {130, 139, 4, 4}, - }}, - {GPT_SUCCESS, {{100, 109, 1, 1}, - {110, 119, 1, 2}, - {120, 129, 2, 3}, - {130, 139, 2, 4}, - }}, - {GPT_ERROR_DUP_GUID, {{100, 109, 1, 1}, - {110, 119, 2, 2}, - {120, 129, 3, 1}, - {130, 139, 4, 4}, - }}, - {GPT_ERROR_DUP_GUID, {{100, 109, 1, 1}, - {110, 119, 1, 2}, - {120, 129, 2, 3}, - {130, 139, 2, 2}, - }}, - }; - - for (i = 0; i < ARRAY_SIZE(cases); ++i) { - BuildTestGptData(gpt); - ZeroEntries(gpt); - for(j = 0; j < ARRAY_SIZE(cases[0].entries); ++j) { - if (!cases[i].entries[j].starting_lba) - break; - - e[j].starting_lba = cases[i].entries[j].starting_lba; - e[j].ending_lba = cases[i].entries[j].ending_lba; - SetGuid(&e[j].type, cases[i].entries[j].type_guid); - SetGuid(&e[j].unique, cases[i].entries[j].unique_guid); - } - RefreshCrc32(gpt); - - EXPECT(cases[i].duplicate == CheckEntries(e, h)); - } - return TEST_OK; -} + /* Known errors are not unknown */ + for (i = 0; i < GPT_ERROR_COUNT; i++) { + EXPECT(GptErrorText(i)); + EXPECT(strcmp(GptErrorText(i), "Unknown")); + } + /* But other error values are */ + EXPECT(!strcmp(GptErrorText(GPT_ERROR_COUNT), "Unknown")); + return TEST_OK; +} /* disable MSVC warnings on unused arguments */ __pragma(warning (disable: 4100)) -int main(int argc, char *argv[]) { - int i; - int error_count = 0; - struct { - char *name; - test_func fp; - int retval; - } test_cases[] = { - { TEST_CASE(StructSizeTest), }, - { TEST_CASE(TestBuildTestGptData), }, - { TEST_CASE(ParameterTests), }, - { TEST_CASE(HeaderCrcTest), }, - { TEST_CASE(SignatureTest), }, - { TEST_CASE(RevisionTest), }, - { TEST_CASE(SizeTest), }, - { TEST_CASE(CrcFieldTest), }, - { TEST_CASE(ReservedFieldsTest), }, - { TEST_CASE(SizeOfPartitionEntryTest), }, - { TEST_CASE(NumberOfPartitionEntriesTest), }, - { TEST_CASE(MyLbaTest), }, - { TEST_CASE(FirstUsableLbaAndLastUsableLbaTest), }, - { TEST_CASE(EntriesCrcTest), }, - { TEST_CASE(ValidEntryTest), }, - { TEST_CASE(OverlappedPartitionTest), }, - { TEST_CASE(SanityCheckTest), }, - { TEST_CASE(NoValidKernelEntryTest), }, - { TEST_CASE(EntryAttributeGetSetTest), }, - { TEST_CASE(EntryTypeTest), }, - { TEST_CASE(GetNextNormalTest), }, - { TEST_CASE(GetNextPrioTest), }, - { TEST_CASE(GetNextTriesTest), }, - { TEST_CASE(GptUpdateTest), }, - { TEST_CASE(UpdateInvalidKernelTypeTest), }, - { TEST_CASE(DuplicateUniqueGuidTest), }, - { TEST_CASE(TestCrc32TestVectors), }, - }; - - for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) { - printf("Running %s() ...\n", test_cases[i].name); - test_cases[i].retval = test_cases[i].fp(); - if (test_cases[i].retval) { - printf(COL_RED "[ERROR]\n\n" COL_STOP); - ++error_count; - } else { - printf(COL_GREEN "[PASS]\n\n" COL_STOP); - } - } - - if (error_count) { - printf("\n--------------------------------------------------\n"); - printf(COL_RED "The following %d test cases are failed:\n" COL_STOP, - error_count); - for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) { - if (test_cases[i].retval) - printf(" %s()\n", test_cases[i].name); - } - } - - return (error_count) ? 1 : 0; +int main(int argc, char *argv[]) +{ + int i; + int error_count = 0; + struct { + char *name; + test_func fp; + int retval; + } test_cases[] = { + { TEST_CASE(StructSizeTest), }, + { TEST_CASE(TestBuildTestGptData), }, + { TEST_CASE(ParameterTests), }, + { TEST_CASE(HeaderCrcTest), }, + { TEST_CASE(HeaderSameTest), }, + { TEST_CASE(SignatureTest), }, + { TEST_CASE(RevisionTest), }, + { TEST_CASE(SizeTest), }, + { TEST_CASE(CrcFieldTest), }, + { TEST_CASE(ReservedFieldsTest), }, + { TEST_CASE(SizeOfPartitionEntryTest), }, + { TEST_CASE(NumberOfPartitionEntriesTest), }, + { TEST_CASE(MyLbaTest), }, + { TEST_CASE(FirstUsableLbaAndLastUsableLbaTest), }, + { TEST_CASE(EntriesCrcTest), }, + { TEST_CASE(ValidEntryTest), }, + { TEST_CASE(OverlappedPartitionTest), }, + { TEST_CASE(SanityCheckTest), }, + { TEST_CASE(NoValidKernelEntryTest), }, + { TEST_CASE(EntryAttributeGetSetTest), }, + { TEST_CASE(EntryTypeTest), }, + { TEST_CASE(GetNextNormalTest), }, + { TEST_CASE(GetNextPrioTest), }, + { TEST_CASE(GetNextTriesTest), }, + { TEST_CASE(GptUpdateTest), }, + { TEST_CASE(UpdateInvalidKernelTypeTest), }, + { TEST_CASE(DuplicateUniqueGuidTest), }, + { TEST_CASE(TestCrc32TestVectors), }, + { TEST_CASE(GetKernelGuidTest), }, + { TEST_CASE(ErrorTextTest), }, + }; + + for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) { + printf("Running %s() ...\n", test_cases[i].name); + test_cases[i].retval = test_cases[i].fp(); + if (test_cases[i].retval) { + printf(COL_RED "[ERROR]\n\n" COL_STOP); + ++error_count; + } else { + printf(COL_GREEN "[PASS]\n\n" COL_STOP); + } + } + + if (error_count) { + printf("\n------------------------------------------------\n"); + printf(COL_RED "The following %d test cases are failed:\n" + COL_STOP, error_count); + for (i = 0; i < sizeof(test_cases)/sizeof(test_cases[0]); ++i) { + if (test_cases[i].retval) + printf(" %s()\n", test_cases[i].name); + } + } + + return error_count ? 1 : 0; } diff --git a/tests/rollback_index2_tests.c b/tests/rollback_index2_tests.c index 4a12a6e2..b670812d 100644 --- a/tests/rollback_index2_tests.c +++ b/tests/rollback_index2_tests.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. * @@ -18,7 +18,8 @@ #include "utility.h" #include "vboot_common.h" -/* Buffer to hold accumulated list of calls to mocked Tlcl functions. +/* + * Buffer to hold accumulated list of calls to mocked Tlcl functions. * Each function appends itself to the buffer and updates mock_cnext. * * Size of mock_calls[] should be big enough to handle all expected @@ -28,13 +29,16 @@ * making all the mock implementations bigger. If this were code used * outside of unit tests we'd want to do that, but here if we did * overrun the buffer the worst that's likely to happen is we'll crash - * the test, and crash = failure anyway. */ + * the test, and crash = failure anyway. + */ static char mock_calls[16384]; static char *mock_cnext = mock_calls; -/* Variables to support mocked error values from Tlcl functions. Each +/* + * Variables to support mocked error values from Tlcl functions. Each * call, mock_count is incremented. If mock_count==fail_at_count, return - * fail_with_error instead of the normal return value. */ + * fail_with_error instead of the normal return value. + */ static int mock_count = 0; static int fail_at_count = 0; static uint32_t fail_with_error = TPM_SUCCESS; @@ -44,7 +48,6 @@ static uint32_t fail_with_error = TPM_SUCCESS; static int noise_count = 0; /* read/write attempt (zero-based) */ static int noise_on[MAX_NOISE_COUNT]; /* calls to inject noise on */ - /* Params / backing store for mocked Tlcl functions. */ static TPM_PERMANENT_FLAGS mock_pflags; static RollbackSpaceFirmware mock_rsf; @@ -52,858 +55,937 @@ static RollbackSpaceKernel mock_rsk; static uint32_t mock_permissions; /* Reset the variables for the Tlcl mock functions. */ -static void ResetMocks(int fail_on_call, uint32_t fail_with_err) { - *mock_calls = 0; - mock_cnext = mock_calls; - mock_count = 0; - fail_at_count = fail_on_call; - fail_with_error = fail_with_err; - noise_count = 0; - Memset(&noise_on, 0, sizeof(noise_on)); - - Memset(&mock_pflags, 0, sizeof(mock_pflags)); - Memset(&mock_rsf, 0, sizeof(mock_rsf)); - Memset(&mock_rsk, 0, sizeof(mock_rsk)); - mock_permissions = 0; +static void ResetMocks(int fail_on_call, uint32_t fail_with_err) +{ + *mock_calls = 0; + mock_cnext = mock_calls; + mock_count = 0; + fail_at_count = fail_on_call; + fail_with_error = fail_with_err; + noise_count = 0; + Memset(&noise_on, 0, sizeof(noise_on)); + + Memset(&mock_pflags, 0, sizeof(mock_pflags)); + Memset(&mock_rsf, 0, sizeof(mock_rsf)); + Memset(&mock_rsk, 0, sizeof(mock_rsk)); + mock_permissions = 0; } /****************************************************************************/ /* Function to garble data on its way to or from the TPM */ -static void MaybeInjectNoise(void* data, uint32_t length) { - if (noise_count < MAX_NOISE_COUNT && noise_on[noise_count]) { - uint8_t *val = data; - val[length-1]++; - } - noise_count++; +static void MaybeInjectNoise(void *data, uint32_t length) +{ + if (noise_count < MAX_NOISE_COUNT && noise_on[noise_count]) { + uint8_t *val = data; + val[length - 1]++; + } + noise_count++; } /****************************************************************************/ /* Mocks for tlcl functions which log the calls made to mock_calls[]. */ -uint32_t TlclLibInit(void) { - mock_cnext += sprintf(mock_cnext, "TlclLibInit()\n"); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclLibInit(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclLibInit()\n"); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclStartup(void) { - mock_cnext += sprintf(mock_cnext, "TlclStartup()\n"); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclStartup(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclStartup()\n"); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclResume(void) { - mock_cnext += sprintf(mock_cnext, "TlclResume()\n"); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclResume(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclResume()\n"); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclForceClear(void) { - mock_cnext += sprintf(mock_cnext, "TlclForceClear()\n"); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclForceClear(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclForceClear()\n"); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclSetEnable(void) { - mock_cnext += sprintf(mock_cnext, "TlclSetEnable()\n"); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclSetEnable(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclSetEnable()\n"); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclSetDeactivated(uint8_t flag) { - mock_cnext += sprintf(mock_cnext, "TlclSetDeactivated(%d)\n", flag); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclSetDeactivated(uint8_t flag) +{ + mock_cnext += sprintf(mock_cnext, "TlclSetDeactivated(%d)\n", flag); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclRead(uint32_t index, void* data, uint32_t length) { - mock_cnext += sprintf(mock_cnext, "TlclRead(0x%x, %d)\n", index, length); - - if (FIRMWARE_NV_INDEX == index) { - TEST_EQ(length, sizeof(mock_rsf), "TlclRead rsf size"); - Memcpy(data, &mock_rsf, length); - MaybeInjectNoise(data, length); - } else if (KERNEL_NV_INDEX == index) { - TEST_EQ(length, sizeof(mock_rsk), "TlclRead rsk size"); - Memcpy(data, &mock_rsk, length); - MaybeInjectNoise(data, length); - } else { - Memset(data, 0, length); - } - - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclRead(uint32_t index, void* data, uint32_t length) +{ + mock_cnext += sprintf(mock_cnext, "TlclRead(0x%x, %d)\n", + index, length); + + if (FIRMWARE_NV_INDEX == index) { + TEST_EQ(length, sizeof(mock_rsf), "TlclRead rsf size"); + Memcpy(data, &mock_rsf, length); + MaybeInjectNoise(data, length); + } else if (KERNEL_NV_INDEX == index) { + TEST_EQ(length, sizeof(mock_rsk), "TlclRead rsk size"); + Memcpy(data, &mock_rsk, length); + MaybeInjectNoise(data, length); + } else { + Memset(data, 0, length); + } + + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclWrite(uint32_t index, const void* data, uint32_t length) { - mock_cnext += sprintf(mock_cnext, "TlclWrite(0x%x, %d)\n", index, length); - - if (FIRMWARE_NV_INDEX == index) { - TEST_EQ(length, sizeof(mock_rsf), "TlclWrite rsf size"); - Memcpy(&mock_rsf, data, length); - MaybeInjectNoise(&mock_rsf, length); - } else if (KERNEL_NV_INDEX == index) { - TEST_EQ(length, sizeof(mock_rsk), "TlclWrite rsk size"); - Memcpy(&mock_rsk, data, length); - MaybeInjectNoise(&mock_rsk, length); - } - - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclWrite(uint32_t index, const void *data, uint32_t length) +{ + mock_cnext += sprintf(mock_cnext, "TlclWrite(0x%x, %d)\n", + index, length); + + if (FIRMWARE_NV_INDEX == index) { + TEST_EQ(length, sizeof(mock_rsf), "TlclWrite rsf size"); + Memcpy(&mock_rsf, data, length); + MaybeInjectNoise(&mock_rsf, length); + } else if (KERNEL_NV_INDEX == index) { + TEST_EQ(length, sizeof(mock_rsk), "TlclWrite rsk size"); + Memcpy(&mock_rsk, data, length); + MaybeInjectNoise(&mock_rsk, length); + } + + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) { - mock_cnext += sprintf(mock_cnext, "TlclDefineSpace(0x%x, 0x%x, %d)\n", - index, perm, size); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) +{ + mock_cnext += sprintf(mock_cnext, "TlclDefineSpace(0x%x, 0x%x, %d)\n", + index, perm, size); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclSelfTestFull(void) { - mock_cnext += sprintf(mock_cnext, "TlclSelfTestFull()\n"); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclSelfTestFull(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclSelfTestFull()\n"); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclContinueSelfTest(void) { - mock_cnext += sprintf(mock_cnext, "TlclContinueSelfTest()\n"); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclContinueSelfTest(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclContinueSelfTest()\n"); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclGetPermanentFlags(TPM_PERMANENT_FLAGS* pflags) { - mock_cnext += sprintf(mock_cnext, "TlclGetPermanentFlags()\n"); - Memcpy(pflags, &mock_pflags, sizeof(mock_pflags)); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclGetPermanentFlags(TPM_PERMANENT_FLAGS *pflags) +{ + mock_cnext += sprintf(mock_cnext, "TlclGetPermanentFlags()\n"); + Memcpy(pflags, &mock_pflags, sizeof(mock_pflags)); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } /* TlclGetFlags() doesn't need mocking; it calls TlclGetPermanentFlags() */ -uint32_t TlclAssertPhysicalPresence(void) { - mock_cnext += sprintf(mock_cnext, "TlclAssertPhysicalPresence()\n"); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclAssertPhysicalPresence(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclAssertPhysicalPresence()\n"); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclFinalizePhysicalPresence(void) { - mock_cnext += sprintf(mock_cnext, "TlclFinalizePhysicalPresence()\n"); - mock_pflags.physicalPresenceLifetimeLock = 1; - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclFinalizePhysicalPresence(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclFinalizePhysicalPresence()\n"); + mock_pflags.physicalPresenceLifetimeLock = 1; + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclPhysicalPresenceCMDEnable(void) { - mock_cnext += sprintf(mock_cnext, "TlclPhysicalPresenceCMDEnable()\n"); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclPhysicalPresenceCMDEnable(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclPhysicalPresenceCMDEnable()\n"); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclSetNvLocked(void) { - mock_cnext += sprintf(mock_cnext, "TlclSetNvLocked()\n"); - mock_pflags.nvLocked = 1; - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclSetNvLocked(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclSetNvLocked()\n"); + mock_pflags.nvLocked = 1; + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclSetGlobalLock(void) { - mock_cnext += sprintf(mock_cnext, "TlclSetGlobalLock()\n"); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclSetGlobalLock(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclSetGlobalLock()\n"); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclLockPhysicalPresence(void) { - mock_cnext += sprintf(mock_cnext, "TlclLockPhysicalPresence()\n"); - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclLockPhysicalPresence(void) +{ + mock_cnext += sprintf(mock_cnext, "TlclLockPhysicalPresence()\n"); + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } -uint32_t TlclGetPermissions(uint32_t index, uint32_t* permissions) { - mock_cnext += sprintf(mock_cnext, "TlclGetPermissions(0x%x)\n", index); - *permissions = mock_permissions; - return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; +uint32_t TlclGetPermissions(uint32_t index, uint32_t* permissions) +{ + mock_cnext += sprintf(mock_cnext, "TlclGetPermissions(0x%x)\n", index); + *permissions = mock_permissions; + return (++mock_count == fail_at_count) ? fail_with_error : TPM_SUCCESS; } - /****************************************************************************/ /* Tests for CRC errors */ -extern uint32_t ReadSpaceFirmware(RollbackSpaceFirmware* rsf); -extern uint32_t WriteSpaceFirmware(RollbackSpaceFirmware* rsf); - -static void CrcTestFirmware(void) { - RollbackSpaceFirmware rsf; - - /* noise on reading, shouldn't matter here because version == 0 */ - ResetMocks(0, 0); - noise_on[0] = 1; - TEST_EQ(ReadSpaceFirmware(&rsf), 0, "ReadSpaceFirmware(), v0"); - TEST_STR_EQ(mock_calls, - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* But if the version >= 2, it will try three times and fail because the CRC - * is no good. */ - ResetMocks(0, 0); - mock_rsf.struct_version = 2; - TEST_EQ(ReadSpaceFirmware(&rsf), TPM_E_CORRUPTED_STATE, - "ReadSpaceFirmware(), v2, bad CRC"); - TEST_STR_EQ(mock_calls, - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* OTOH, if the CRC is good and some noise happens, it should recover. */ - ResetMocks(0, 0); - mock_rsf.struct_version = 2; - mock_rsf.crc8 = Crc8(&mock_rsf, offsetof(RollbackSpaceFirmware, crc8)); - noise_on[0] = 1; - TEST_EQ(ReadSpaceFirmware(&rsf), 0, "ReadSpaceFirmware(), v2, good CRC"); - TEST_STR_EQ(mock_calls, - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* A write with version < 2 should convert to v2 and create the CRC */ - ResetMocks(0, 0); - Memset(&rsf, 0, sizeof(rsf)); - TEST_EQ(WriteSpaceFirmware(&rsf), 0, "WriteSpaceFirmware(), v0"); - TEST_EQ(mock_rsf.struct_version, 2, "WriteSpaceFirmware(), check v2"); - TEST_STR_EQ(mock_calls, - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* Same as above, but with some noise during the readback */ - ResetMocks(0, 0); - Memset(&rsf, 0, sizeof(rsf)); - noise_on[1] = 1; - noise_on[2] = 1; - TEST_EQ(WriteSpaceFirmware(&rsf), 0, "WriteSpaceFirmware(), read noise"); - TEST_STR_EQ(mock_calls, - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* With noise during the write, we'll try the write again */ - ResetMocks(0, 0); - Memset(&rsf, 0, sizeof(rsf)); - noise_on[0] = 1; - TEST_EQ(WriteSpaceFirmware(&rsf), 0, "WriteSpaceFirmware(), write noise"); - TEST_EQ(mock_rsf.struct_version, 2, "WriteSpaceFirmware(), check v2"); - TEST_STR_EQ(mock_calls, - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* Only if it just keeps on failing forever do we eventually give up */ - ResetMocks(0, 0); - Memset(&rsf, 0, sizeof(rsf)); - Memset(noise_on, 1, sizeof(noise_on)); - TEST_EQ(WriteSpaceFirmware(&rsf), TPM_E_CORRUPTED_STATE, - "WriteSpaceFirmware(), always noise"); - TEST_STR_EQ(mock_calls, - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); +extern uint32_t ReadSpaceFirmware(RollbackSpaceFirmware *rsf); +extern uint32_t WriteSpaceFirmware(RollbackSpaceFirmware *rsf); + +static void CrcTestFirmware(void) +{ + RollbackSpaceFirmware rsf; + + /* Noise on reading, shouldn't matter here because version == 0 */ + ResetMocks(0, 0); + noise_on[0] = 1; + TEST_EQ(ReadSpaceFirmware(&rsf), 0, "ReadSpaceFirmware(), v0"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* + * But if the version >= 2, it will try three times and fail because + * the CRC is no good. + */ + ResetMocks(0, 0); + mock_rsf.struct_version = 2; + TEST_EQ(ReadSpaceFirmware(&rsf), TPM_E_CORRUPTED_STATE, + "ReadSpaceFirmware(), v2, bad CRC"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* If the CRC is good and some noise happens, it should recover. */ + ResetMocks(0, 0); + mock_rsf.struct_version = 2; + mock_rsf.crc8 = Crc8(&mock_rsf, offsetof(RollbackSpaceFirmware, crc8)); + noise_on[0] = 1; + TEST_EQ(ReadSpaceFirmware(&rsf), 0, + "ReadSpaceFirmware(), v2, good CRC"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* A write with version < 2 should convert to v2 and create the CRC */ + ResetMocks(0, 0); + Memset(&rsf, 0, sizeof(rsf)); + TEST_EQ(WriteSpaceFirmware(&rsf), 0, "WriteSpaceFirmware(), v0"); + TEST_EQ(mock_rsf.struct_version, 2, "WriteSpaceFirmware(), check v2"); + TEST_STR_EQ(mock_calls, + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* Same as above, but with some noise during the readback */ + ResetMocks(0, 0); + Memset(&rsf, 0, sizeof(rsf)); + noise_on[1] = 1; + noise_on[2] = 1; + TEST_EQ(WriteSpaceFirmware(&rsf), 0, + "WriteSpaceFirmware(), read noise"); + TEST_STR_EQ(mock_calls, + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* With noise during the write, we'll try the write again */ + ResetMocks(0, 0); + Memset(&rsf, 0, sizeof(rsf)); + noise_on[0] = 1; + TEST_EQ(WriteSpaceFirmware(&rsf), 0, + "WriteSpaceFirmware(), write noise"); + TEST_EQ(mock_rsf.struct_version, 2, "WriteSpaceFirmware(), check v2"); + TEST_STR_EQ(mock_calls, + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* Only if it just keeps on failing forever do we eventually give up */ + ResetMocks(0, 0); + Memset(&rsf, 0, sizeof(rsf)); + Memset(noise_on, 1, sizeof(noise_on)); + TEST_EQ(WriteSpaceFirmware(&rsf), TPM_E_CORRUPTED_STATE, + "WriteSpaceFirmware(), always noise"); + TEST_STR_EQ(mock_calls, + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); } -extern uint32_t ReadSpaceKernel(RollbackSpaceKernel* rsk); -extern uint32_t WriteSpaceKernel(RollbackSpaceKernel* rsk); - -static void CrcTestKernel(void) { - RollbackSpaceKernel rsk; - - /* noise on reading, shouldn't matter here because version == 0 */ - ResetMocks(0, 0); - noise_on[0] = 1; - TEST_EQ(ReadSpaceKernel(&rsk), 0, "ReadSpaceKernel(), v0"); - TEST_STR_EQ(mock_calls, - "TlclRead(0x1008, 13)\n", - "tlcl calls"); - - /* But if the version >= 2, it will try three times and fail because the CRC - * is no good. */ - ResetMocks(0, 0); - mock_rsk.struct_version = 2; - TEST_EQ(ReadSpaceKernel(&rsk), TPM_E_CORRUPTED_STATE, - "ReadSpaceKernel(), v2, bad CRC"); - TEST_STR_EQ(mock_calls, - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n", - "tlcl calls"); - - /* OTOH, if the CRC is good and some noise happens, it should recover. */ - ResetMocks(0, 0); - mock_rsk.struct_version = 2; - mock_rsk.crc8 = Crc8(&mock_rsk, offsetof(RollbackSpaceKernel, crc8)); - noise_on[0] = 1; - TEST_EQ(ReadSpaceKernel(&rsk), 0, "ReadSpaceKernel(), v2, good CRC"); - TEST_STR_EQ(mock_calls, - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n", - "tlcl calls"); - - /* A write with version < 2 should convert to v2 and create the CRC */ - ResetMocks(0, 0); - Memset(&rsk, 0, sizeof(rsk)); - TEST_EQ(WriteSpaceKernel(&rsk), 0, "WriteSpaceKernel(), v0"); - TEST_EQ(mock_rsk.struct_version, 2, "WriteSpaceKernel(), check v2"); - TEST_STR_EQ(mock_calls, - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n", - "tlcl calls"); - - /* Same as above, but with some noise during the readback */ - ResetMocks(0, 0); - Memset(&rsk, 0, sizeof(rsk)); - noise_on[1] = 1; - noise_on[2] = 1; - TEST_EQ(WriteSpaceKernel(&rsk), 0, "WriteSpaceKernel(), read noise"); - TEST_STR_EQ(mock_calls, - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n", - "tlcl calls"); - - /* With noise during the write, we'll try the write again */ - ResetMocks(0, 0); - Memset(&rsk, 0, sizeof(rsk)); - noise_on[0] = 1; - TEST_EQ(WriteSpaceKernel(&rsk), 0, "WriteSpaceKernel(), write noise"); - TEST_EQ(mock_rsk.struct_version, 2, "WriteSpaceKernel(), check v2"); - TEST_STR_EQ(mock_calls, - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n", - "tlcl calls"); - - /* Only if it just keeps on failing forever do we eventually give up */ - ResetMocks(0, 0); - Memset(&rsk, 0, sizeof(rsk)); - Memset(noise_on, 1, sizeof(noise_on)); - TEST_EQ(WriteSpaceKernel(&rsk), TPM_E_CORRUPTED_STATE, - "WriteSpaceKernel(), always noise"); - TEST_STR_EQ(mock_calls, - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n", - "tlcl calls"); +extern uint32_t ReadSpaceKernel(RollbackSpaceKernel *rsk); +extern uint32_t WriteSpaceKernel(RollbackSpaceKernel *rsk); + +static void CrcTestKernel(void) +{ + RollbackSpaceKernel rsk; + + /* Noise on reading shouldn't matter here because version == 0 */ + ResetMocks(0, 0); + noise_on[0] = 1; + TEST_EQ(ReadSpaceKernel(&rsk), 0, "ReadSpaceKernel(), v0"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1008, 13)\n", + "tlcl calls"); + + /* + * But if the version >= 2, it will try three times and fail because + * the CRC is no good. + */ + ResetMocks(0, 0); + mock_rsk.struct_version = 2; + TEST_EQ(ReadSpaceKernel(&rsk), TPM_E_CORRUPTED_STATE, + "ReadSpaceKernel(), v2, bad CRC"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n", + "tlcl calls"); + + /* If the CRC is good and some noise happens, it should recover. */ + ResetMocks(0, 0); + mock_rsk.struct_version = 2; + mock_rsk.crc8 = Crc8(&mock_rsk, offsetof(RollbackSpaceKernel, crc8)); + noise_on[0] = 1; + TEST_EQ(ReadSpaceKernel(&rsk), 0, "ReadSpaceKernel(), v2, good CRC"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n", + "tlcl calls"); + + /* A write with version < 2 should convert to v2 and create the CRC */ + ResetMocks(0, 0); + Memset(&rsk, 0, sizeof(rsk)); + TEST_EQ(WriteSpaceKernel(&rsk), 0, "WriteSpaceKernel(), v0"); + TEST_EQ(mock_rsk.struct_version, 2, "WriteSpaceKernel(), check v2"); + TEST_STR_EQ(mock_calls, + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n", + "tlcl calls"); + + /* Same as above, but with some noise during the readback */ + ResetMocks(0, 0); + Memset(&rsk, 0, sizeof(rsk)); + noise_on[1] = 1; + noise_on[2] = 1; + TEST_EQ(WriteSpaceKernel(&rsk), 0, "WriteSpaceKernel(), read noise"); + TEST_STR_EQ(mock_calls, + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n", + "tlcl calls"); + + /* With noise during the write, we'll try the write again */ + ResetMocks(0, 0); + Memset(&rsk, 0, sizeof(rsk)); + noise_on[0] = 1; + TEST_EQ(WriteSpaceKernel(&rsk), 0, "WriteSpaceKernel(), write noise"); + TEST_EQ(mock_rsk.struct_version, 2, "WriteSpaceKernel(), check v2"); + TEST_STR_EQ(mock_calls, + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n", + "tlcl calls"); + + /* Only if it just keeps on failing forever do we eventually give up */ + ResetMocks(0, 0); + Memset(&rsk, 0, sizeof(rsk)); + Memset(noise_on, 1, sizeof(noise_on)); + TEST_EQ(WriteSpaceKernel(&rsk), TPM_E_CORRUPTED_STATE, + "WriteSpaceKernel(), always noise"); + TEST_STR_EQ(mock_calls, + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n", + "tlcl calls"); } /****************************************************************************/ /* Tests for misc helper functions */ -static void MiscTest(void) { - uint8_t buf[8]; - - ResetMocks(0, 0); - TEST_EQ(TPMClearAndReenable(), 0, "TPMClearAndReenable()"); - TEST_STR_EQ(mock_calls, - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n", - "tlcl calls"); - - ResetMocks(0, 0); - TEST_EQ(SafeWrite(0x123, buf, 8), 0, "SafeWrite()"); - TEST_STR_EQ(mock_calls, - "TlclWrite(0x123, 8)\n", - "tlcl calls"); - - ResetMocks(1, TPM_E_BADINDEX); - TEST_EQ(SafeWrite(0x123, buf, 8), TPM_E_BADINDEX, "SafeWrite() bad"); - TEST_STR_EQ(mock_calls, - "TlclWrite(0x123, 8)\n", - "tlcl calls"); - - ResetMocks(1, TPM_E_MAXNVWRITES); - TEST_EQ(SafeWrite(0x123, buf, 8), 0, "SafeWrite() retry max writes"); - TEST_STR_EQ(mock_calls, - "TlclWrite(0x123, 8)\n" - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n" - "TlclWrite(0x123, 8)\n", - "tlcl calls"); - - ResetMocks(0, 0); - TEST_EQ(SafeDefineSpace(0x123, 6, 8), 0, "SafeDefineSpace()"); - TEST_STR_EQ(mock_calls, - "TlclDefineSpace(0x123, 0x6, 8)\n", - "tlcl calls"); - - ResetMocks(1, TPM_E_BADINDEX); - TEST_EQ(SafeDefineSpace(0x123, 6, 8), TPM_E_BADINDEX, - "SafeDefineSpace() bad"); - TEST_STR_EQ(mock_calls, - "TlclDefineSpace(0x123, 0x6, 8)\n", - "tlcl calls"); - - ResetMocks(1, TPM_E_MAXNVWRITES); - TEST_EQ(SafeDefineSpace(0x123, 6, 8), 0, - "SafeDefineSpace() retry max writes"); - TEST_STR_EQ(mock_calls, - "TlclDefineSpace(0x123, 0x6, 8)\n" - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n" - "TlclDefineSpace(0x123, 0x6, 8)\n", - "tlcl calls"); +static void MiscTest(void) +{ + uint8_t buf[8]; + + ResetMocks(0, 0); + TEST_EQ(TPMClearAndReenable(), 0, "TPMClearAndReenable()"); + TEST_STR_EQ(mock_calls, + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n", + "tlcl calls"); + + ResetMocks(0, 0); + TEST_EQ(SafeWrite(0x123, buf, 8), 0, "SafeWrite()"); + TEST_STR_EQ(mock_calls, + "TlclWrite(0x123, 8)\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_BADINDEX); + TEST_EQ(SafeWrite(0x123, buf, 8), TPM_E_BADINDEX, "SafeWrite() bad"); + TEST_STR_EQ(mock_calls, + "TlclWrite(0x123, 8)\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_MAXNVWRITES); + TEST_EQ(SafeWrite(0x123, buf, 8), 0, "SafeWrite() retry max writes"); + TEST_STR_EQ(mock_calls, + "TlclWrite(0x123, 8)\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + "TlclWrite(0x123, 8)\n", + "tlcl calls"); + + ResetMocks(0, 0); + TEST_EQ(SafeDefineSpace(0x123, 6, 8), 0, "SafeDefineSpace()"); + TEST_STR_EQ(mock_calls, + "TlclDefineSpace(0x123, 0x6, 8)\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_BADINDEX); + TEST_EQ(SafeDefineSpace(0x123, 6, 8), TPM_E_BADINDEX, + "SafeDefineSpace() bad"); + TEST_STR_EQ(mock_calls, + "TlclDefineSpace(0x123, 0x6, 8)\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_MAXNVWRITES); + TEST_EQ(SafeDefineSpace(0x123, 6, 8), 0, + "SafeDefineSpace() retry max writes"); + TEST_STR_EQ(mock_calls, + "TlclDefineSpace(0x123, 0x6, 8)\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + "TlclDefineSpace(0x123, 0x6, 8)\n", + "tlcl calls"); } /****************************************************************************/ /* Tests for one-time initialization */ -static void OneTimeInitTest(void) { - RollbackSpaceFirmware rsf; - RollbackSpaceKernel rsk; - - /* Complete initialization */ - ResetMocks(0, 0); - TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), 0, "OneTimeInitializeTPM()"); - TEST_STR_EQ(mock_calls, - "TlclSelfTestFull()\n" - "TlclGetPermanentFlags()\n" - "TlclFinalizePhysicalPresence()\n" - "TlclSetNvLocked()\n" - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n" - /* kernel space */ - "TlclDefineSpace(0x1008, 0x1, 13)\n" - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - /* firmware space */ - "TlclDefineSpace(0x1007, 0x8001, 10)\n" - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - TEST_EQ(mock_rsf.struct_version, ROLLBACK_SPACE_FIRMWARE_VERSION, "rsf ver"); - TEST_EQ(mock_rsf.flags, 0, "rsf flags"); - TEST_EQ(mock_rsf.fw_versions, 0, "rsf fw_versions"); - TEST_EQ(mock_rsk.struct_version, ROLLBACK_SPACE_KERNEL_VERSION, "rsk ver"); - TEST_EQ(mock_rsk.uid, ROLLBACK_SPACE_KERNEL_UID, "rsk uid"); - TEST_EQ(mock_rsk.kernel_versions, 0, "rsk kernel_versions"); - - /* Physical presence already initialized */ - ResetMocks(0, 0); - mock_pflags.physicalPresenceLifetimeLock = 1; - TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), 0, "OneTimeInitializeTPM()"); - TEST_STR_EQ(mock_calls, - "TlclSelfTestFull()\n" - "TlclGetPermanentFlags()\n" - "TlclSetNvLocked()\n" - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n" - /* kernel space */ - "TlclDefineSpace(0x1008, 0x1, 13)\n" - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - /* firmware space */ - "TlclDefineSpace(0x1007, 0x8001, 10)\n" - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* NV locking already initialized */ - ResetMocks(0, 0); - mock_pflags.nvLocked = 1; - TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), 0, "OneTimeInitializeTPM()"); - TEST_STR_EQ(mock_calls, - "TlclSelfTestFull()\n" - "TlclGetPermanentFlags()\n" - "TlclFinalizePhysicalPresence()\n" - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n" - /* kernel space */ - "TlclDefineSpace(0x1008, 0x1, 13)\n" - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - /* firmware space */ - "TlclDefineSpace(0x1007, 0x8001, 10)\n" - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* Self test error */ - ResetMocks(1, TPM_E_IOERROR); - TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), TPM_E_IOERROR, - "OneTimeInitializeTPM() selftest"); - TEST_STR_EQ(mock_calls, - "TlclSelfTestFull()\n", - "tlcl calls"); +static void OneTimeInitTest(void) +{ + RollbackSpaceFirmware rsf; + RollbackSpaceKernel rsk; + + /* Complete initialization */ + ResetMocks(0, 0); + TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), 0, "OneTimeInitializeTPM()"); + TEST_STR_EQ(mock_calls, + "TlclSelfTestFull()\n" + "TlclGetPermanentFlags()\n" + "TlclFinalizePhysicalPresence()\n" + "TlclSetNvLocked()\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + /* kernel space */ + "TlclDefineSpace(0x1008, 0x1, 13)\n" + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + /* firmware space */ + "TlclDefineSpace(0x1007, 0x8001, 10)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + TEST_EQ(mock_rsf.struct_version, ROLLBACK_SPACE_FIRMWARE_VERSION, + "rsf ver"); + TEST_EQ(mock_rsf.flags, 0, "rsf flags"); + TEST_EQ(mock_rsf.fw_versions, 0, "rsf fw_versions"); + TEST_EQ(mock_rsk.struct_version, ROLLBACK_SPACE_KERNEL_VERSION, + "rsk ver"); + TEST_EQ(mock_rsk.uid, ROLLBACK_SPACE_KERNEL_UID, "rsk uid"); + TEST_EQ(mock_rsk.kernel_versions, 0, "rsk kernel_versions"); + + /* Physical presence already initialized */ + ResetMocks(0, 0); + mock_pflags.physicalPresenceLifetimeLock = 1; + TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), 0, "OneTimeInitializeTPM()"); + TEST_STR_EQ(mock_calls, + "TlclSelfTestFull()\n" + "TlclGetPermanentFlags()\n" + "TlclSetNvLocked()\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + /* kernel space */ + "TlclDefineSpace(0x1008, 0x1, 13)\n" + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + /* firmware space */ + "TlclDefineSpace(0x1007, 0x8001, 10)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* NV locking already initialized */ + ResetMocks(0, 0); + mock_pflags.nvLocked = 1; + TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), 0, "OneTimeInitializeTPM()"); + TEST_STR_EQ(mock_calls, + "TlclSelfTestFull()\n" + "TlclGetPermanentFlags()\n" + "TlclFinalizePhysicalPresence()\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + /* kernel space */ + "TlclDefineSpace(0x1008, 0x1, 13)\n" + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + /* firmware space */ + "TlclDefineSpace(0x1007, 0x8001, 10)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* Self test error */ + ResetMocks(1, TPM_E_IOERROR); + TEST_EQ(OneTimeInitializeTPM(&rsf, &rsk), TPM_E_IOERROR, + "OneTimeInitializeTPM() selftest"); + TEST_STR_EQ(mock_calls, + "TlclSelfTestFull()\n", + "tlcl calls"); } /****************************************************************************/ /* Tests for TPM setup */ -static void SetupTpmTest(void) { - RollbackSpaceFirmware rsf; - - /* Complete setup */ - ResetMocks(0, 0); - TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), 0, "SetupTPM()"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* If TPM is disabled or deactivated, must enable it */ - ResetMocks(0, 0); - mock_pflags.disable = 1; - TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), TPM_E_MUST_REBOOT, "SetupTPM() disabled"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n", - "tlcl calls"); - - ResetMocks(0, 0); - mock_pflags.deactivated = 1; - TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), TPM_E_MUST_REBOOT, - "SetupTPM() deactivated"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n", - "tlcl calls"); - - /* If physical presence command isn't enabled, should try to enable it */ - ResetMocks(3, TPM_E_IOERROR); - TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), 0, "SetupTPM() pp cmd"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclPhysicalPresenceCMDEnable()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* If firmware space is missing, do one-time init */ - ResetMocks(5, TPM_E_BADINDEX); - mock_pflags.physicalPresenceLifetimeLock = 1; - mock_pflags.nvLocked = 1; - TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), 0, "SetupTPM() no firmware space"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclRead(0x1007, 10)\n" - /* Calls from one-time init */ - "TlclSelfTestFull()\n" - "TlclGetPermanentFlags()\n" - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n" - "TlclDefineSpace(0x1008, 0x1, 13)\n" - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n" - "TlclDefineSpace(0x1007, 0x8001, 10)\n" - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* Other firmware space error is passed through */ - ResetMocks(5, TPM_E_IOERROR); - TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), TPM_E_CORRUPTED_STATE, - "SetupTPM() bad firmware space"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - /* If developer flag has toggled, clear ownership and write new flag */ - ResetMocks(0, 0); - TEST_EQ(SetupTPM(0, 1, 0, 0, &rsf), 0, "SetupTPM() to dev"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclRead(0x1007, 10)\n" - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n" - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - TEST_EQ(mock_rsf.flags, FLAG_LAST_BOOT_DEVELOPER, "fw space flags to dev 1"); - - ResetMocks(0, 0); - mock_rsf.flags = FLAG_LAST_BOOT_DEVELOPER; - TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), 0, "SetupTPM() from dev"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclRead(0x1007, 10)\n" - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n" - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - TEST_EQ(mock_rsf.flags, 0, "fw space flags from dev 1"); - - /* If TPM clear request, clear ownership also */ - ResetMocks(0, 0); - TEST_EQ(SetupTPM(0, 0, 0, 1, &rsf), 0, "SetupTPM() clear owner"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclRead(0x1007, 10)\n" - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n", - "tlcl calls"); - - /* Note: SetupTPM() recovery_mode parameter sets a global flag in - * rollback_index.c; this is tested along with RollbackKernelLock() below. */ +static void SetupTpmTest(void) +{ + RollbackSpaceFirmware rsf; + + /* Complete setup */ + ResetMocks(0, 0); + TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), 0, "SetupTPM()"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* If TPM is disabled or deactivated, must enable it */ + ResetMocks(0, 0); + mock_pflags.disable = 1; + TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), TPM_E_MUST_REBOOT, + "SetupTPM() disabled"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n", + "tlcl calls"); + + ResetMocks(0, 0); + mock_pflags.deactivated = 1; + TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), TPM_E_MUST_REBOOT, + "SetupTPM() deactivated"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n", + "tlcl calls"); + + /* If physical presence command isn't enabled, try to enable it */ + ResetMocks(3, TPM_E_IOERROR); + TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), 0, "SetupTPM() pp cmd"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclPhysicalPresenceCMDEnable()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* If firmware space is missing, do one-time init */ + ResetMocks(5, TPM_E_BADINDEX); + mock_pflags.physicalPresenceLifetimeLock = 1; + mock_pflags.nvLocked = 1; + TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), 0, "SetupTPM() no firmware space"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclRead(0x1007, 10)\n" + /* Calls from one-time init */ + "TlclSelfTestFull()\n" + "TlclGetPermanentFlags()\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + "TlclDefineSpace(0x1008, 0x1, 13)\n" + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n" + "TlclDefineSpace(0x1007, 0x8001, 10)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* Other firmware space error is passed through */ + ResetMocks(5, TPM_E_IOERROR); + TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), TPM_E_CORRUPTED_STATE, + "SetupTPM() bad firmware space"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* If developer flag has toggled, clear ownership and write new flag */ + ResetMocks(0, 0); + TEST_EQ(SetupTPM(0, 1, 0, 0, &rsf), 0, "SetupTPM() to dev"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclRead(0x1007, 10)\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + TEST_EQ(mock_rsf.flags, FLAG_LAST_BOOT_DEVELOPER, + "fw space flags to dev 1"); + + ResetMocks(0, 0); + mock_rsf.flags = FLAG_LAST_BOOT_DEVELOPER; + TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), 0, "SetupTPM() from dev"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclRead(0x1007, 10)\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + TEST_EQ(mock_rsf.flags, 0, "fw space flags from dev 1"); + + /* If TPM clear request, clear ownership also */ + ResetMocks(0, 0); + TEST_EQ(SetupTPM(0, 0, 0, 1, &rsf), 0, "SetupTPM() clear owner"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclRead(0x1007, 10)\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n", + "tlcl calls"); + + /* Handle request to clear virtual dev switch */ + ResetMocks(0, 0); + mock_rsf.flags = FLAG_VIRTUAL_DEV_MODE_ON | FLAG_LAST_BOOT_DEVELOPER; + TEST_EQ(SetupTPM(0, 0, 1, 0, &rsf), 0, "SetupTPM() clear virtual dev"); + TEST_EQ(mock_rsf.flags, 0, "Clear virtual dev"); + + /* If virtual dev switch is on, that should set last boot developer */ + ResetMocks(0, 0); + mock_rsf.flags = FLAG_VIRTUAL_DEV_MODE_ON; + SetupTPM(0, 0, 0, 0, &rsf); + TEST_EQ(mock_rsf.flags, + FLAG_VIRTUAL_DEV_MODE_ON | FLAG_LAST_BOOT_DEVELOPER, + "virtual dev sets last boot"); + + /* + * Note: SetupTPM() recovery_mode parameter sets a global flag in + * rollback_index.c; this is tested along with RollbackKernelLock() + * below. + */ } /****************************************************************************/ /* Tests for RollbackFirmware() calls */ -static void RollbackFirmwareTest(void) { - uint32_t version; - int dev_mode; - - /* Normal setup */ - ResetMocks(0, 0); - dev_mode = 0; - version = 123; - mock_rsf.fw_versions = 0x12345678; - TEST_EQ(RollbackFirmwareSetup(0, 0, dev_mode, 0, &dev_mode, &version), 0, - "RollbackFirmwareSetup()"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - TEST_EQ(version, 0x12345678, "RollbackFirmwareSetup() version"); - - /* Error during setup should clear version */ - ResetMocks(1, TPM_E_IOERROR); - dev_mode = 0; - version = 123; - mock_rsf.fw_versions = 0x12345678; - TEST_EQ(RollbackFirmwareSetup(0, 0, dev_mode, 0, &dev_mode, &version), - TPM_E_IOERROR, - "RollbackFirmwareSetup() error"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n", - "tlcl calls"); - TEST_EQ(version, 0, "RollbackFirmwareSetup() version on error"); - - /* Developer mode flag gets passed properly */ - ResetMocks(0, 0); - dev_mode = 1; - TEST_EQ(RollbackFirmwareSetup(0, dev_mode, 0, 0, &dev_mode, &version), 0, - "RollbackFirmwareSetup() to dev"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclRead(0x1007, 10)\n" - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n" - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - TEST_EQ(mock_rsf.flags, FLAG_LAST_BOOT_DEVELOPER, "fw space flags to dev 2"); - - /* So does clear-TPM request */ - ResetMocks(0, 0); - dev_mode = 0; - TEST_EQ(RollbackFirmwareSetup(0, dev_mode, 0, 1, &dev_mode, &version), 0, - "RollbackFirmwareSetup() clear owner"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclStartup()\n" - "TlclAssertPhysicalPresence()\n" - "TlclGetPermanentFlags()\n" - "TlclRead(0x1007, 10)\n" - "TlclForceClear()\n" - "TlclSetEnable()\n" - "TlclSetDeactivated(0)\n", - "tlcl calls"); - - /* Test write */ - ResetMocks(0, 0); - TEST_EQ(RollbackFirmwareWrite(0xBEAD1234), 0, "RollbackFirmwareWrite()"); - TEST_EQ(mock_rsf.fw_versions, 0xBEAD1234, "RollbackFirmwareWrite() version"); - TEST_STR_EQ(mock_calls, - "TlclRead(0x1007, 10)\n" - "TlclWrite(0x1007, 10)\n" - "TlclRead(0x1007, 10)\n", - "tlcl calls"); - - ResetMocks(1, TPM_E_IOERROR); - TEST_EQ(RollbackFirmwareWrite(123), TPM_E_IOERROR, - "RollbackFirmwareWrite() error"); - - /* Test lock */ - ResetMocks(0, 0); - TEST_EQ(RollbackFirmwareLock(), 0, "RollbackFirmwareLock()"); - TEST_STR_EQ(mock_calls, - "TlclSetGlobalLock()\n", - "tlcl calls"); - - ResetMocks(1, TPM_E_IOERROR); - TEST_EQ(RollbackFirmwareLock(), TPM_E_IOERROR, - "RollbackFirmwareLock() error"); + +static void RollbackFirmwareTest(void) +{ + uint32_t version; + int dev_mode; + + /* Normal setup */ + ResetMocks(0, 0); + dev_mode = 0; + version = 123; + mock_rsf.fw_versions = 0x12345678; + TEST_EQ(RollbackFirmwareSetup(0, 0, dev_mode, 0, &dev_mode, &version), + 0, "RollbackFirmwareSetup()"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + TEST_EQ(version, 0x12345678, "RollbackFirmwareSetup() version"); + + /* Error during setup should clear version */ + ResetMocks(1, TPM_E_IOERROR); + dev_mode = 0; + version = 123; + mock_rsf.fw_versions = 0x12345678; + TEST_EQ(RollbackFirmwareSetup(0, 0, dev_mode, 0, &dev_mode, &version), + TPM_E_IOERROR, + "RollbackFirmwareSetup() error"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n", + "tlcl calls"); + TEST_EQ(version, 0, "RollbackFirmwareSetup() version on error"); + + /* Developer mode flag gets passed properly */ + ResetMocks(0, 0); + dev_mode = 1; + TEST_EQ(RollbackFirmwareSetup(0, dev_mode, 0, 0, &dev_mode, &version), + 0, "RollbackFirmwareSetup() to dev"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclRead(0x1007, 10)\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + TEST_EQ(mock_rsf.flags, FLAG_LAST_BOOT_DEVELOPER, + "fw space flags to dev 2"); + + /* So does clear-TPM request */ + ResetMocks(0, 0); + dev_mode = 0; + TEST_EQ(RollbackFirmwareSetup(0, dev_mode, 0, 1, &dev_mode, &version), + 0, "RollbackFirmwareSetup() clear owner"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclStartup()\n" + "TlclAssertPhysicalPresence()\n" + "TlclGetPermanentFlags()\n" + "TlclRead(0x1007, 10)\n" + "TlclForceClear()\n" + "TlclSetEnable()\n" + "TlclSetDeactivated(0)\n", + "tlcl calls"); + + /* Test write */ + ResetMocks(0, 0); + TEST_EQ(RollbackFirmwareWrite(0xBEAD1234), 0, + "RollbackFirmwareWrite()"); + TEST_EQ(mock_rsf.fw_versions, 0xBEAD1234, + "RollbackFirmwareWrite() version"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1007, 10)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_IOERROR); + TEST_EQ(RollbackFirmwareWrite(123), TPM_E_IOERROR, + "RollbackFirmwareWrite() error"); + + /* Test setting virtual dev mode */ + ResetMocks(0, 0); + TEST_EQ(SetVirtualDevMode(1), 0, "SetVirtualDevMode(1)"); + TEST_EQ(mock_rsf.flags, FLAG_VIRTUAL_DEV_MODE_ON, "Virtual dev on"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1007, 10)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + ResetMocks(0, 0); + TEST_EQ(SetVirtualDevMode(0), 0, "SetVirtualDevMode(0)"); + TEST_EQ(mock_rsf.flags, 0, "Virtual dev off"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1007, 10)\n" + "TlclWrite(0x1007, 10)\n" + "TlclRead(0x1007, 10)\n", + "tlcl calls"); + + /* Test lock */ + ResetMocks(0, 0); + TEST_EQ(RollbackFirmwareLock(), 0, "RollbackFirmwareLock()"); + TEST_STR_EQ(mock_calls, + "TlclSetGlobalLock()\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_IOERROR); + TEST_EQ(RollbackFirmwareLock(), TPM_E_IOERROR, + "RollbackFirmwareLock() error"); } /****************************************************************************/ /* Tests for RollbackKernel() calls */ -static void RollbackKernelTest(void) { - RollbackSpaceFirmware rsf; - uint32_t version = 0; - - /* RollbackKernel*() functions use a global flag inside - * rollback_index.c based on recovery mode, which is set by - * SetupTPM(). Clear the flag for the first set of tests. */ - TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), 0, "SetupTPM()"); - - /* Normal read */ - ResetMocks(0, 0); - mock_rsk.uid = ROLLBACK_SPACE_KERNEL_UID; - mock_permissions = TPM_NV_PER_PPWRITE; - mock_rsk.kernel_versions = 0x87654321; - TEST_EQ(RollbackKernelRead(&version), 0, "RollbackKernelRead()"); - TEST_STR_EQ(mock_calls, - "TlclRead(0x1008, 13)\n" - "TlclGetPermissions(0x1008)\n", - "tlcl calls"); - TEST_EQ(version, 0x87654321, "RollbackKernelRead() version"); - - /* Read error */ - ResetMocks(1, TPM_E_IOERROR); - TEST_EQ(RollbackKernelRead(&version), TPM_E_IOERROR, - "RollbackKernelRead() error"); - TEST_STR_EQ(mock_calls, - "TlclRead(0x1008, 13)\n", - "tlcl calls"); - - /* Wrong permission or UID will return error */ - ResetMocks(0, 0); - mock_rsk.uid = ROLLBACK_SPACE_KERNEL_UID + 1; - mock_permissions = TPM_NV_PER_PPWRITE; - TEST_EQ(RollbackKernelRead(&version), TPM_E_CORRUPTED_STATE, - "RollbackKernelRead() bad uid"); - - ResetMocks(0, 0); - mock_rsk.uid = ROLLBACK_SPACE_KERNEL_UID; - mock_permissions = TPM_NV_PER_PPWRITE + 1; - TEST_EQ(RollbackKernelRead(&version), TPM_E_CORRUPTED_STATE, - "RollbackKernelRead() bad permissions"); - - /* Test write */ - ResetMocks(0, 0); - TEST_EQ(RollbackKernelWrite(0xBEAD4321), 0, "RollbackKernelWrite()"); - TEST_EQ(mock_rsk.kernel_versions, 0xBEAD4321, - "RollbackKernelWrite() version"); - TEST_STR_EQ(mock_calls, - "TlclRead(0x1008, 13)\n" - "TlclWrite(0x1008, 13)\n" - "TlclRead(0x1008, 13)\n", - "tlcl calls"); - - ResetMocks(1, TPM_E_IOERROR); - TEST_EQ(RollbackKernelWrite(123), TPM_E_IOERROR, - "RollbackKernelWrite() error"); - - /* Test lock (recovery off) */ - ResetMocks(0, 0); - TEST_EQ(RollbackKernelLock(), 0, "RollbackKernelLock()"); - TEST_STR_EQ(mock_calls, - "TlclLockPhysicalPresence()\n", - "tlcl calls"); - - ResetMocks(1, TPM_E_IOERROR); - TEST_EQ(RollbackKernelLock(), TPM_E_IOERROR, "RollbackKernelLock() error"); - - /* Test lock with recovery on; shouldn't lock PP */ - SetupTPM(1, 0, 0, 0, &rsf); - ResetMocks(0, 0); - TEST_EQ(RollbackKernelLock(), 0, "RollbackKernelLock() in recovery"); - TEST_STR_EQ(mock_calls, "", "no tlcl calls"); +static void RollbackKernelTest(void) +{ + RollbackSpaceFirmware rsf; + uint32_t version = 0; + + /* + * RollbackKernel*() functions use a global flag inside + * rollback_index.c based on recovery mode, which is set by SetupTPM(). + * Clear the flag for the first set of tests. + */ + TEST_EQ(SetupTPM(0, 0, 0, 0, &rsf), 0, "SetupTPM()"); + + /* Normal read */ + ResetMocks(0, 0); + mock_rsk.uid = ROLLBACK_SPACE_KERNEL_UID; + mock_permissions = TPM_NV_PER_PPWRITE; + mock_rsk.kernel_versions = 0x87654321; + TEST_EQ(RollbackKernelRead(&version), 0, "RollbackKernelRead()"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1008, 13)\n" + "TlclGetPermissions(0x1008)\n", + "tlcl calls"); + TEST_EQ(version, 0x87654321, "RollbackKernelRead() version"); + + /* Read error */ + ResetMocks(1, TPM_E_IOERROR); + TEST_EQ(RollbackKernelRead(&version), TPM_E_IOERROR, + "RollbackKernelRead() error"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1008, 13)\n", + "tlcl calls"); + + /* Wrong permission or UID will return error */ + ResetMocks(0, 0); + mock_rsk.uid = ROLLBACK_SPACE_KERNEL_UID + 1; + mock_permissions = TPM_NV_PER_PPWRITE; + TEST_EQ(RollbackKernelRead(&version), TPM_E_CORRUPTED_STATE, + "RollbackKernelRead() bad uid"); + + ResetMocks(0, 0); + mock_rsk.uid = ROLLBACK_SPACE_KERNEL_UID; + mock_permissions = TPM_NV_PER_PPWRITE + 1; + TEST_EQ(RollbackKernelRead(&version), TPM_E_CORRUPTED_STATE, + "RollbackKernelRead() bad permissions"); + + /* Test write */ + ResetMocks(0, 0); + TEST_EQ(RollbackKernelWrite(0xBEAD4321), 0, "RollbackKernelWrite()"); + TEST_EQ(mock_rsk.kernel_versions, 0xBEAD4321, + "RollbackKernelWrite() version"); + TEST_STR_EQ(mock_calls, + "TlclRead(0x1008, 13)\n" + "TlclWrite(0x1008, 13)\n" + "TlclRead(0x1008, 13)\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_IOERROR); + TEST_EQ(RollbackKernelWrite(123), TPM_E_IOERROR, + "RollbackKernelWrite() error"); + + /* Test lock (recovery off) */ + ResetMocks(0, 0); + TEST_EQ(RollbackKernelLock(), 0, "RollbackKernelLock()"); + TEST_STR_EQ(mock_calls, + "TlclLockPhysicalPresence()\n", + "tlcl calls"); + + ResetMocks(1, TPM_E_IOERROR); + TEST_EQ(RollbackKernelLock(), TPM_E_IOERROR, + "RollbackKernelLock() error"); + + /* Test lock with recovery on; shouldn't lock PP */ + SetupTPM(1, 0, 0, 0, &rsf); + ResetMocks(0, 0); + TEST_EQ(RollbackKernelLock(), 0, "RollbackKernelLock() in recovery"); + TEST_STR_EQ(mock_calls, "", "no tlcl calls"); } /* Tests for RollbackS3Resume() */ -static void RollbackS3ResumeTest(void) { - - ResetMocks(0, 0); - TEST_EQ(RollbackS3Resume(), 0, "RollbackS3Resume()"); - TEST_STR_EQ(mock_calls, - "TlclLibInit()\n" - "TlclResume()\n", - "tlcl calls"); - - /* Should ignore postinit error */ - ResetMocks(2, TPM_E_INVALID_POSTINIT); - TEST_EQ(RollbackS3Resume(), 0, "RollbackS3Resume() postinit"); - - /* Resume with other error */ - ResetMocks(2, TPM_E_IOERROR); - TEST_EQ(RollbackS3Resume(), TPM_E_IOERROR, "RollbackS3Resume() other error"); +static void RollbackS3ResumeTest(void) +{ + ResetMocks(0, 0); + TEST_EQ(RollbackS3Resume(), 0, "RollbackS3Resume()"); + TEST_STR_EQ(mock_calls, + "TlclLibInit()\n" + "TlclResume()\n", + "tlcl calls"); + + /* Should ignore postinit error */ + ResetMocks(2, TPM_E_INVALID_POSTINIT); + TEST_EQ(RollbackS3Resume(), 0, "RollbackS3Resume() postinit"); + + /* Resume with other error */ + ResetMocks(2, TPM_E_IOERROR); + TEST_EQ(RollbackS3Resume(), TPM_E_IOERROR, + "RollbackS3Resume() other error"); } /* disable MSVC warnings on unused arguments */ __pragma(warning (disable: 4100)) -int main(int argc, char* argv[]) { - int error_code = 0; - - CrcTestFirmware(); - CrcTestKernel(); - MiscTest(); - OneTimeInitTest(); - SetupTpmTest(); - RollbackFirmwareTest(); - RollbackKernelTest(); - RollbackS3ResumeTest(); - - if (!gTestSuccess) - error_code = 255; - - return error_code; +int main(int argc, char* argv[]) +{ + CrcTestFirmware(); + CrcTestKernel(); + MiscTest(); + OneTimeInitTest(); + SetupTpmTest(); + RollbackFirmwareTest(); + RollbackKernelTest(); + RollbackS3ResumeTest(); + + return gTestSuccess ? 0 : 255; } diff --git a/tests/rollback_index3_tests.c b/tests/rollback_index3_tests.c new file mode 100644 index 00000000..ece8bc08 --- /dev/null +++ b/tests/rollback_index3_tests.c @@ -0,0 +1,45 @@ +/* Copyright (c) 2013 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 do-nothing rollback_index functions with disabled TPM + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define _STUB_IMPLEMENTATION_ /* So we can use memset() ourselves */ + +#include "rollback_index.h" +#include "test_common.h" + +/* disable MSVC warnings on unused arguments */ +__pragma(warning (disable: 4100)) + +int main(int argc, char* argv[]) +{ + int is_virt_dev; + uint32_t version; + + TEST_EQ(RollbackS3Resume(), 0, "RollbackS3Resume()"); + + is_virt_dev = 1; + version = 1; + TEST_EQ(RollbackFirmwareSetup(0, 0, 0, 0, &is_virt_dev, &version), + 0, "RollbackFirmwareSetup()"); + TEST_EQ(is_virt_dev, 0, "rfs is_virt_dev"); + TEST_EQ(version, 0, "rfs version"); + + TEST_EQ(RollbackFirmwareWrite(0), 0, "RollbackFirmwareWrite()"); + TEST_EQ(RollbackFirmwareLock(), 0, "RollbackFirmwareLock()"); + + version = 1; + TEST_EQ(RollbackKernelRead(&version), 0, "RollbackKernelRead()"); + TEST_EQ(version, 0, "rkr version"); + + TEST_EQ(RollbackKernelWrite(0), 0, "RollbackKernelWrite()"); + TEST_EQ(RollbackKernelLock(), 0, "RollbackKernelLock()"); + + return gTestSuccess ? 0 : 255; +} diff --git a/tests/run_vboot_ec_tests.sh b/tests/run_vboot_ec_tests.sh deleted file mode 100755 index 302a3578..00000000 --- a/tests/run_vboot_ec_tests.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -eu - -# 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. - -# Run verified boot firmware and kernel verification tests. - -# Load common constants and variables. -. "$(dirname "$0")/common.sh" - -check_test_keys - -for priv in ${TESTKEY_DIR}/*.vbprivk; do - root=$(basename ${priv%.vbprivk}) - pub="${priv%.vbprivk}.vbpubk" - echo "Trying $root ..." - ${TEST_DIR}/vboot_ec_tests "$priv" "$pub" -done diff --git a/tests/test_using_qemu.sh b/tests/test_using_qemu.sh new file mode 100755 index 00000000..6b3f0733 --- /dev/null +++ b/tests/test_using_qemu.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# +# Copyright (c) 2013 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. +# +# Script to run a test under qemu +# +# Usage: +# test_using_qemu.sh (command line to run) +# +# Required environment variables: +# BUILD_RUN - path to build directory inside chroot +# HOME - home directory inside chroot +# QEMU_RUN - path to QEMU binary inside chroot +# SYSROOT - path to root for target platform, outside chroot + +set -e + +# Set up mounts +sudo mkdir -p "${SYSROOT}/proc" "${SYSROOT}/dev" +sudo mount --bind /proc "${SYSROOT}/proc" +sudo mount --bind /dev "${SYSROOT}/dev" + +# Don't exit on error, so we can capture the error code +set +e +sudo chroot ${SYSROOT} ${QEMU_RUN} -drop-ld-preload \ + -E LD_LIBRARY_PATH=/lib64:/lib:/usr/lib64:/usr/lib \ + -E HOME=${HOME} \ + -E BUILD=${BUILD_RUN} \ + -- $* +exit_code=$? +set -e + +# Clean up mounts +sudo umount -l "${SYSROOT}/proc" +sudo umount -l "${SYSROOT}/dev" + +# Pass through exit code from command +exit $exit_code diff --git a/tests/vboot_api_init_tests.c b/tests/vboot_api_init_tests.c index dd6daee4..bde10a7c 100644 --- a/tests/vboot_api_init_tests.c +++ b/tests/vboot_api_init_tests.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. * @@ -21,7 +21,7 @@ static VbCommonParams cparams; static VbInitParams iparams; static VbNvContext vnc; static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE]; -static VbSharedDataHeader* shared = (VbSharedDataHeader*)shared_data; +static VbSharedDataHeader *shared = (VbSharedDataHeader *)shared_data; static uint64_t mock_timer; static int rollback_s3_retval; static int nv_write_called; @@ -29,371 +29,496 @@ static GoogleBinaryBlockHeader gbb; static int mock_virt_dev_sw; static uint32_t mock_tpm_version; static uint32_t mock_rfs_retval; +static int rfs_clear_tpm_request; +static int rfs_disable_dev_request; /* Reset mock data (for use before each test) */ -static void ResetMocks(void) { - Memset(&cparams, 0, sizeof(cparams)); - cparams.shared_data_size = sizeof(shared_data); - cparams.shared_data_blob = shared_data; - cparams.gbb_data = &gbb; +static void ResetMocks(void) +{ + Memset(&cparams, 0, sizeof(cparams)); + cparams.shared_data_size = sizeof(shared_data); + cparams.shared_data_blob = shared_data; + cparams.gbb_data = &gbb; - Memset(&gbb, 0, sizeof(gbb)); - gbb.major_version = GBB_MAJOR_VER; - gbb.minor_version = GBB_MINOR_VER; - gbb.flags = 0; + Memset(&gbb, 0, sizeof(gbb)); + gbb.major_version = GBB_MAJOR_VER; + gbb.minor_version = GBB_MINOR_VER; + gbb.flags = 0; - Memset(&iparams, 0, sizeof(iparams)); + Memset(&iparams, 0, sizeof(iparams)); - Memset(&vnc, 0, sizeof(vnc)); - VbNvSetup(&vnc); - VbNvTeardown(&vnc); /* So CRC gets generated */ + Memset(&vnc, 0, sizeof(vnc)); + VbNvSetup(&vnc); + VbNvTeardown(&vnc); /* So CRC gets generated */ - Memset(&shared_data, 0, sizeof(shared_data)); - VbSharedDataInit(shared, sizeof(shared_data)); + Memset(&shared_data, 0, sizeof(shared_data)); + VbSharedDataInit(shared, sizeof(shared_data)); - mock_timer = 10; - rollback_s3_retval = TPM_SUCCESS; - nv_write_called = 0; + mock_timer = 10; + rollback_s3_retval = TPM_SUCCESS; + nv_write_called = 0; - mock_virt_dev_sw = 0; - mock_tpm_version = 0x10001; - mock_rfs_retval = 0; + mock_virt_dev_sw = 0; + mock_tpm_version = 0x10001; + mock_rfs_retval = 0; + + rfs_clear_tpm_request = 0; + rfs_disable_dev_request = 0; } /****************************************************************************/ /* Mocked verification functions */ -VbError_t VbExNvStorageRead(uint8_t* buf) { - Memcpy(buf, vnc.raw, sizeof(vnc.raw)); - return VBERROR_SUCCESS; +VbError_t VbExNvStorageRead(uint8_t *buf) +{ + Memcpy(buf, vnc.raw, sizeof(vnc.raw)); + return VBERROR_SUCCESS; } -VbError_t VbExNvStorageWrite(const uint8_t* buf) { - nv_write_called = 1; - Memcpy(vnc.raw, buf, sizeof(vnc.raw)); - return VBERROR_SUCCESS; +VbError_t VbExNvStorageWrite(const uint8_t *buf) +{ + nv_write_called = 1; + Memcpy(vnc.raw, buf, sizeof(vnc.raw)); + return VBERROR_SUCCESS; } -uint64_t VbExGetTimer(void) { - /* Exponential-ish rather than linear time, so that subtracting any - * two mock values will yield a unique result. */ - uint64_t new_timer = mock_timer * 2 + 1; - VbAssert(new_timer > mock_timer); /* Make sure we don't overflow */ - mock_timer = new_timer; - return mock_timer; +uint64_t VbExGetTimer(void) +{ + /* + * Exponential-ish rather than linear time, so that subtracting any + * two mock values will yield a unique result. + */ + uint64_t new_timer = mock_timer * 2 + 1; + VbAssert(new_timer > mock_timer); /* Make sure we don't overflow */ + mock_timer = new_timer; + return mock_timer; } -uint32_t RollbackS3Resume(void) { - return rollback_s3_retval; +uint32_t RollbackS3Resume(void) +{ + return rollback_s3_retval; } uint32_t RollbackFirmwareSetup(int recovery_mode, int is_hw_dev, int disable_dev_request, int clear_tpm_owner_request, /* two outputs on success */ - int *is_virt_dev, uint32_t *version) { - *is_virt_dev = mock_virt_dev_sw; - *version = mock_tpm_version; - return mock_rfs_retval; + int *is_virt_dev, uint32_t *version) +{ + rfs_clear_tpm_request = clear_tpm_owner_request; + rfs_disable_dev_request = disable_dev_request; + + *is_virt_dev = mock_virt_dev_sw; + *version = mock_tpm_version; + return mock_rfs_retval; } /****************************************************************************/ /* Test VbInit() and check expected return value and recovery reason */ + static void TestVbInit(VbError_t expected_retval, - uint8_t expected_recovery, const char* desc) { - uint32_t rr = 256; + uint8_t expected_recovery, const char *desc) +{ + uint32_t rr = 256; - TEST_EQ(VbInit(&cparams, &iparams), expected_retval, desc); - VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &rr); - TEST_EQ(rr, expected_recovery, " (recovery request)"); + TEST_EQ(VbInit(&cparams, &iparams), expected_retval, desc); + VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &rr); + TEST_EQ(rr, expected_recovery, " (recovery request)"); } /****************************************************************************/ -static void VbInitTest(void) { - uint32_t u; - - /* Test passing in too small a shared data area */ - ResetMocks(); - cparams.shared_data_size = VB_SHARED_DATA_MIN_SIZE - 1; - TestVbInit(VBERROR_INIT_SHARED_DATA, 0, "Shared data too small"); - - /* Normal call; dev=0 rec=0 */ - ResetMocks(); - TestVbInit(0, 0, "Normal call"); - TEST_EQ(shared->timer_vb_init_enter, 21, " time enter"); - TEST_EQ(shared->timer_vb_init_exit, 43, " time exit"); - TEST_EQ(shared->flags, 0, " shared flags"); - TEST_EQ(iparams.out_flags, 0, " out flags"); - TEST_EQ(nv_write_called, 0, " NV write not called since nothing changed"); - - /* If NV data is trashed, we initialize it */ - ResetMocks(); - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); - /* Note that we're not doing a VbNvTeardown(), so the CRC hasn't - * been regenerated yet. So VbInit() should ignore the corrupted - * recovery value and boot normally. */ - TestVbInit(0, 0, "NV data trashed"); - TEST_EQ(nv_write_called, 1, " NV write called"); - - /* Test boot switch flags which are just passed through to shared - * flags, and don't have an effect on VbInit(). */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_WP_ENABLED; - TestVbInit(0, 0, "Flags test WP"); - TEST_EQ(shared->flags, VBSD_BOOT_FIRMWARE_WP_ENABLED, " shared flags WP"); - - ResetMocks(); - iparams.flags = VB_INIT_FLAG_SW_WP_ENABLED; - TestVbInit(0, 0, "Flags test SW WP"); - TEST_EQ(shared->flags, VBSD_BOOT_FIRMWARE_SW_WP_ENABLED, - " shared flags SW WP"); - - ResetMocks(); - iparams.flags = VB_INIT_FLAG_RO_NORMAL_SUPPORT; - TestVbInit(0, 0, " flags test RO normal"); - TEST_EQ(shared->flags, VBSD_BOOT_RO_NORMAL_SUPPORT, - " shared flags RO normal"); - - /* S3 resume */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_S3_RESUME; - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); - VbNvTeardown(&vnc); - /* S3 resume doesn't clear the recovery request (or act on it) */ - TestVbInit(0, 123, "S3 resume"); - TEST_EQ(shared->flags, VBSD_BOOT_S3_RESUME, " shared flags S3"); - TEST_EQ(iparams.out_flags, 0, " out flags"); - TEST_EQ(shared->recovery_reason, 0, " S3 doesn't look at recovery request"); - - /* S3 resume with TPM resume error */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_S3_RESUME; - rollback_s3_retval = 1; - /* S3 resume doesn't clear the recovery request (or act on it) */ - TestVbInit(VBERROR_TPM_S3_RESUME, 0, "S3 resume rollback error"); - - /* Normal boot doesn't care about TPM resume error because it - * doesn't call RollbackS3Resume() */ - ResetMocks(); - rollback_s3_retval = 1; - TestVbInit(0, 0, "Normal doesn't S3 resume"); - - /* S3 resume with debug reset */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_S3_RESUME; - VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 1); - VbNvTeardown(&vnc); - TestVbInit(0, 0, "S3 debug reset"); - TEST_EQ(iparams.out_flags, VB_INIT_OUT_S3_DEBUG_BOOT, " out flags"); - VbNvGet(&vnc, VBNV_DEBUG_RESET_MODE, &u); - TEST_EQ(u, 0, " S3 clears nv debug reset mode"); - - /* Normal boot clears S3 debug reset mode, but doesn't set output flag */ - ResetMocks(); - VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 1); - VbNvTeardown(&vnc); - TestVbInit(0, 0, "Normal with debug reset mode"); - TEST_EQ(iparams.out_flags, 0, " out flags"); - VbNvGet(&vnc, VBNV_DEBUG_RESET_MODE, &u); - TEST_EQ(u, 0, " normal clears nv debug reset mode"); - - /* S3 resume with debug reset is a normal boot, so doesn't resume the TPM */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_S3_RESUME; - rollback_s3_retval = 1; - VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 1); - VbNvTeardown(&vnc); - TestVbInit(0, 0, "S3 debug reset rollback error"); - - /* Developer mode */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_DEV_SWITCH_ON; - TestVbInit(0, 0, "Dev mode on"); - TEST_EQ(shared->recovery_reason, 0, " recovery reason"); - TEST_EQ(iparams.out_flags, - VB_INIT_OUT_CLEAR_RAM | - VB_INIT_OUT_ENABLE_DISPLAY | - VB_INIT_OUT_ENABLE_USB_STORAGE | - VB_INIT_OUT_ENABLE_DEVELOPER | - VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); - TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags"); - - /* Developer mode forced by GBB flag */ - ResetMocks(); - iparams.flags = 0; - gbb.flags = GBB_FLAG_FORCE_DEV_SWITCH_ON; - TestVbInit(0, 0, "Dev mode via GBB"); - TEST_EQ(shared->recovery_reason, 0, " recovery reason"); - TEST_EQ(iparams.out_flags, - VB_INIT_OUT_CLEAR_RAM | - VB_INIT_OUT_ENABLE_DISPLAY | - VB_INIT_OUT_ENABLE_USB_STORAGE | - VB_INIT_OUT_ENABLE_DEVELOPER | - VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); - TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags"); - - /* Recovery mode from NV storage */ - ResetMocks(); - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); - VbNvTeardown(&vnc); - TestVbInit(0, 0, "Recovery mode - from nv"); - TEST_EQ(shared->recovery_reason, 123, " recovery reason"); - TEST_EQ(iparams.out_flags, - VB_INIT_OUT_ENABLE_RECOVERY | - VB_INIT_OUT_CLEAR_RAM | - VB_INIT_OUT_ENABLE_DISPLAY | - VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); - TEST_EQ(shared->flags, 0, " shared flags"); - - /* Recovery mode from recovery button */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED; - TestVbInit(0, 0, "Recovery mode - button"); - TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_MANUAL, - " recovery reason"); - TEST_EQ(iparams.out_flags, - VB_INIT_OUT_ENABLE_RECOVERY | - VB_INIT_OUT_CLEAR_RAM | - VB_INIT_OUT_ENABLE_DISPLAY | - VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); - TEST_EQ(shared->flags, VBSD_BOOT_REC_SWITCH_ON, " shared flags"); - - /* Recovery button reason supersedes NV reason */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED; - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); - VbNvTeardown(&vnc); - TestVbInit(0, 0, "Recovery mode - button AND nv"); - TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_MANUAL, - " recovery reason"); - - /* Recovery mode from previous boot fail */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_PREVIOUS_BOOT_FAIL; - TestVbInit(0, 0, "Recovery mode - previous boot fail"); - TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_FIRMWARE, - " recovery reason"); - TEST_EQ(iparams.out_flags, - VB_INIT_OUT_ENABLE_RECOVERY | - VB_INIT_OUT_CLEAR_RAM | - VB_INIT_OUT_ENABLE_DISPLAY | - VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); - TEST_EQ(shared->flags, 0, " shared flags"); - - /* Recovery mode from NV supersedes previous boot fail */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_PREVIOUS_BOOT_FAIL; - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); - VbNvTeardown(&vnc); - TestVbInit(0, 0, "Recovery mode - previous boot fail AND nv"); - TEST_EQ(shared->recovery_reason, 123, " recovery reason"); - - /* Dev + recovery = recovery */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED | VB_INIT_FLAG_DEV_SWITCH_ON; - TestVbInit(0, 0, "Recovery mode - button"); - TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_MANUAL, - " recovery reason"); - TEST_EQ(iparams.out_flags, - VB_INIT_OUT_ENABLE_RECOVERY | - VB_INIT_OUT_CLEAR_RAM | - VB_INIT_OUT_ENABLE_DISPLAY | - VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); - TEST_EQ(shared->flags, - VBSD_BOOT_REC_SWITCH_ON | VBSD_BOOT_DEV_SWITCH_ON, " shared flags"); +static void VbInitTest(void) +{ + uint32_t u; + + /* Test passing in too small a shared data area */ + ResetMocks(); + cparams.shared_data_size = VB_SHARED_DATA_MIN_SIZE - 1; + TestVbInit(VBERROR_INIT_SHARED_DATA, 0, "Shared data too small"); + + /* Normal call; dev=0 rec=0 */ + ResetMocks(); + TestVbInit(0, 0, "Normal call"); + TEST_EQ(shared->timer_vb_init_enter, 21, " time enter"); + TEST_EQ(shared->timer_vb_init_exit, 43, " time exit"); + TEST_EQ(shared->flags, 0, " shared flags"); + TEST_EQ(iparams.out_flags, 0, " out flags"); + TEST_EQ(nv_write_called, 0, + " NV write not called since nothing changed"); + + /* If NV data is trashed, we initialize it */ + ResetMocks(); + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); + /* + * Note that we're not doing a VbNvTeardown(), so the CRC hasn't been + * regenerated yet. So VbInit() should ignore the corrupted recovery + * value and boot normally. + */ + TestVbInit(0, 0, "NV data trashed"); + TEST_EQ(nv_write_called, 1, " NV write called"); + + /* + * Test boot switch flags which are just passed through to shared + * flags, and don't have an effect on VbInit(). + */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_WP_ENABLED; + TestVbInit(0, 0, "Flags test WP"); + TEST_EQ(shared->flags, VBSD_BOOT_FIRMWARE_WP_ENABLED, + " shared flags"); + + ResetMocks(); + iparams.flags = VB_INIT_FLAG_SW_WP_ENABLED; + TestVbInit(0, 0, "Flags test SW WP"); + TEST_EQ(shared->flags, VBSD_BOOT_FIRMWARE_SW_WP_ENABLED, + " shared flags"); + + ResetMocks(); + iparams.flags = VB_INIT_FLAG_RO_NORMAL_SUPPORT; + TestVbInit(0, 0, " flags test RO normal"); + TEST_EQ(shared->flags, VBSD_BOOT_RO_NORMAL_SUPPORT, + " shared flags"); + + ResetMocks(); + iparams.flags = VB_INIT_FLAG_EC_SOFTWARE_SYNC; + TestVbInit(0, 0, " flags test EC software sync"); + TEST_EQ(shared->flags, VBSD_EC_SOFTWARE_SYNC, " shared flags"); + + ResetMocks(); + iparams.flags = VB_INIT_FLAG_EC_SLOW_UPDATE; + TestVbInit(0, 0, " flags test EC slow update"); + TEST_EQ(shared->flags, VBSD_EC_SLOW_UPDATE, " shared flags"); + + /* S3 resume */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_S3_RESUME; + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); + VbNvTeardown(&vnc); + /* S3 resume doesn't clear the recovery request (or act on it) */ + TestVbInit(0, 123, "S3 resume"); + TEST_EQ(shared->flags, VBSD_BOOT_S3_RESUME, " shared flags S3"); + TEST_EQ(iparams.out_flags, 0, " out flags"); + TEST_EQ(shared->recovery_reason, 0, + " S3 doesn't look at recovery request"); + + /* S3 resume with TPM resume error */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_S3_RESUME; + rollback_s3_retval = 1; + /* S3 resume doesn't clear the recovery request (or act on it) */ + TestVbInit(VBERROR_TPM_S3_RESUME, 0, "S3 resume rollback error"); + + /* + * Normal boot doesn't care about TPM resume error because it doesn't + * call RollbackS3Resume(). + */ + ResetMocks(); + rollback_s3_retval = 1; + TestVbInit(0, 0, "Normal doesn't S3 resume"); + + /* S3 resume with debug reset */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_S3_RESUME; + VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 1); + VbNvTeardown(&vnc); + TestVbInit(0, 0, "S3 debug reset"); + TEST_EQ(iparams.out_flags, VB_INIT_OUT_S3_DEBUG_BOOT, " out flags"); + VbNvGet(&vnc, VBNV_DEBUG_RESET_MODE, &u); + TEST_EQ(u, 0, " S3 clears nv debug reset mode"); + + /* Normal boot clears S3 debug reset mode; doesn't set output flag */ + ResetMocks(); + VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 1); + VbNvTeardown(&vnc); + TestVbInit(0, 0, "Normal with debug reset mode"); + TEST_EQ(iparams.out_flags, 0, " out flags"); + VbNvGet(&vnc, VBNV_DEBUG_RESET_MODE, &u); + TEST_EQ(u, 0, " normal clears nv debug reset mode"); + + /* + * S3 resume with debug reset is a normal boot, so doesn't resume the + * TPM. + */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_S3_RESUME; + rollback_s3_retval = 1; + VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 1); + VbNvTeardown(&vnc); + TestVbInit(0, 0, "S3 debug reset rollback error"); + + /* Developer mode */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_DEV_SWITCH_ON; + TestVbInit(0, 0, "Dev mode on"); + TEST_EQ(shared->recovery_reason, 0, " recovery reason"); + TEST_EQ(iparams.out_flags, + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE | + VB_INIT_OUT_ENABLE_DEVELOPER | + VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); + TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags"); + + /* Developer mode forced by GBB flag */ + ResetMocks(); + iparams.flags = 0; + gbb.flags = GBB_FLAG_FORCE_DEV_SWITCH_ON; + TestVbInit(0, 0, "Dev mode via GBB"); + TEST_EQ(shared->recovery_reason, 0, " recovery reason"); + TEST_EQ(iparams.out_flags, + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE | + VB_INIT_OUT_ENABLE_DEVELOPER | + VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); + TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags"); + + /* Developer mode when option ROM matters and isn't loaded */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_DEV_SWITCH_ON | + VB_INIT_FLAG_OPROM_MATTERS; + TestVbInit(VBERROR_VGA_OPROM_MISMATCH, 0, "Dev mode need oprom"); + VbNvGet(&vnc, VBNV_OPROM_NEEDED, &u); + TEST_EQ(u, 1, " oprom requested"); + + /* Developer mode when option ROM matters and is already loaded */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_DEV_SWITCH_ON | + VB_INIT_FLAG_OPROM_MATTERS | VB_INIT_FLAG_OPROM_LOADED; + TestVbInit(0, 0, "Dev mode has oprom"); + + /* Normal mode when option ROM matters and is loaded */ + ResetMocks(); + VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1); + VbNvTeardown(&vnc); + iparams.flags = VB_INIT_FLAG_OPROM_MATTERS | VB_INIT_FLAG_OPROM_LOADED; + TestVbInit(VBERROR_VGA_OPROM_MISMATCH, 0, "Normal mode with oprom"); + VbNvGet(&vnc, VBNV_OPROM_NEEDED, &u); + TEST_EQ(u, 0, " oprom not requested"); + + /* Option ROMs can be forced by GBB flag */ + ResetMocks(); + gbb.flags = GBB_FLAG_LOAD_OPTION_ROMS; + TestVbInit(0, 0, "GBB load option ROMs"); + TEST_EQ(iparams.out_flags, VB_INIT_OUT_ENABLE_OPROM, " out flags"); + + /* If requiring signed only, don't enable alternate OS by default */ + ResetMocks(); + VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 1); + VbNvTeardown(&vnc); + iparams.flags = VB_INIT_FLAG_DEV_SWITCH_ON; + TestVbInit(0, 0, "Dev signed only"); + TEST_EQ(iparams.out_flags, + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE | + VB_INIT_OUT_ENABLE_DEVELOPER, " out flags"); + + /* But that can be overridden by the GBB */ + ResetMocks(); + VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 1); + VbNvTeardown(&vnc); + iparams.flags = VB_INIT_FLAG_DEV_SWITCH_ON; + gbb.flags = GBB_FLAG_ENABLE_ALTERNATE_OS; + TestVbInit(0, 0, "Force option ROMs via GBB"); + TEST_EQ(iparams.out_flags, + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE | + VB_INIT_OUT_ENABLE_DEVELOPER | + VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); + + /* The GBB override is ignored in normal mode */ + ResetMocks(); + gbb.flags = GBB_FLAG_ENABLE_ALTERNATE_OS; + TestVbInit(0, 0, "Normal mode ignores forcing option ROMs via GBB"); + TEST_EQ(iparams.out_flags, 0, " out flags"); + + /* Recovery mode from NV storage */ + ResetMocks(); + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); + VbNvTeardown(&vnc); + TestVbInit(0, 0, "Recovery mode - from nv"); + TEST_EQ(shared->recovery_reason, 123, " recovery reason"); + TEST_EQ(iparams.out_flags, + VB_INIT_OUT_ENABLE_RECOVERY | + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); + TEST_EQ(shared->flags, 0, " shared flags"); + + /* Recovery mode from recovery button */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED; + TestVbInit(0, 0, "Recovery mode - button"); + TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_MANUAL, + " recovery reason"); + TEST_EQ(iparams.out_flags, + VB_INIT_OUT_ENABLE_RECOVERY | + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); + TEST_EQ(shared->flags, VBSD_BOOT_REC_SWITCH_ON, " shared flags"); + + /* Recovery button reason supersedes NV reason */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED; + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); + VbNvTeardown(&vnc); + TestVbInit(0, 0, "Recovery mode - button AND nv"); + TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_MANUAL, + " recovery reason"); + + /* Recovery mode from previous boot fail */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_PREVIOUS_BOOT_FAIL; + TestVbInit(0, 0, "Recovery mode - previous boot fail"); + TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_FIRMWARE, + " recovery reason"); + TEST_EQ(iparams.out_flags, + VB_INIT_OUT_ENABLE_RECOVERY | + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); + TEST_EQ(shared->flags, 0, " shared flags"); + + /* Recovery mode from NV supersedes previous boot fail */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_PREVIOUS_BOOT_FAIL; + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); + VbNvTeardown(&vnc); + TestVbInit(0, 0, "Recovery mode - previous boot fail AND nv"); + TEST_EQ(shared->recovery_reason, 123, " recovery reason"); + + /* Dev + recovery = recovery */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED | + VB_INIT_FLAG_DEV_SWITCH_ON; + TestVbInit(0, 0, "Recovery mode - button"); + TEST_EQ(shared->recovery_reason, VBNV_RECOVERY_RO_MANUAL, + " recovery reason"); + TEST_EQ(iparams.out_flags, + VB_INIT_OUT_ENABLE_RECOVERY | + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE, " out flags"); + TEST_EQ(shared->flags, + VBSD_BOOT_REC_SWITCH_ON | VBSD_BOOT_DEV_SWITCH_ON, + " shared flags"); } -static void VbInitTestTPM(void) { - - /* Rollback setup needs to reboot */ - ResetMocks(); - mock_rfs_retval = TPM_E_MUST_REBOOT; - TestVbInit(VBERROR_TPM_REBOOT_REQUIRED, 0, "Rollback TPM reboot (rec=0)"); - ResetMocks(); - mock_rfs_retval = TPM_E_MUST_REBOOT; - iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED; - TestVbInit(VBERROR_TPM_REBOOT_REQUIRED, VBNV_RECOVERY_RO_TPM_REBOOT, - "Rollback TPM reboot, in recovery, first time"); - /* Ignore if we already tried rebooting */ - ResetMocks(); - mock_rfs_retval = TPM_E_MUST_REBOOT; - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_REBOOT); - VbNvTeardown(&vnc); - TestVbInit(0, 0, "Rollback TPM reboot, in recovery, already retried"); - TEST_EQ(shared->fw_version_tpm, 0x10001, " shared fw_version_tpm"); - - /* Other rollback setup errors */ - ResetMocks(); - mock_rfs_retval = TPM_E_IOERROR; - mock_tpm_version = 0x20002; - TestVbInit(VBERROR_TPM_FIRMWARE_SETUP, VBNV_RECOVERY_RO_TPM_S_ERROR, - "Rollback TPM setup error - not in recovery"); - TEST_EQ(shared->fw_version_tpm, 0, " shared fw_version_tpm not set"); - ResetMocks(); - mock_rfs_retval = TPM_E_IOERROR; - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_US_TEST); - VbNvTeardown(&vnc); - TestVbInit(0, 0, "Rollback TPM setup error ignored in recovery"); - TEST_EQ(shared->fw_version_tpm, 0x10001, " shared fw_version_tpm"); - - /* Virtual developer switch, but not enabled. */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_VIRTUAL_DEV_SWITCH; - TestVbInit(0, 0, "TPM Dev mode off"); - TEST_EQ(shared->recovery_reason, 0, " recovery reason"); - TEST_EQ(iparams.out_flags, 0, " out flags"); - TEST_EQ(shared->flags, VBSD_HONOR_VIRT_DEV_SWITCH, " shared flags"); - - /* Virtual developer switch, enabled. */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_VIRTUAL_DEV_SWITCH; - mock_virt_dev_sw = 1; - TestVbInit(0, 0, "TPM Dev mode on"); - TEST_EQ(shared->recovery_reason, 0, " recovery reason"); - TEST_EQ(iparams.out_flags, - VB_INIT_OUT_CLEAR_RAM | - VB_INIT_OUT_ENABLE_DISPLAY | - VB_INIT_OUT_ENABLE_USB_STORAGE | - VB_INIT_OUT_ENABLE_DEVELOPER | - VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); - TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON | VBSD_HONOR_VIRT_DEV_SWITCH, - " shared flags"); - - /* Ignore virtual developer switch, even though enabled. */ - ResetMocks(); - mock_virt_dev_sw = 1; - TestVbInit(0, 0, "TPM Dev mode on but ignored"); - TEST_EQ(shared->recovery_reason, 0, " recovery reason"); - TEST_EQ(iparams.out_flags, 0, " out flags"); - TEST_EQ(shared->flags, 0, " shared flags"); - - /* HW dev switch on, no virtual developer switch */ - ResetMocks(); - iparams.flags = VB_INIT_FLAG_DEV_SWITCH_ON; - TestVbInit(0, 0, "HW Dev mode on"); - TEST_EQ(shared->recovery_reason, 0, " recovery reason"); - TEST_EQ(iparams.out_flags, - VB_INIT_OUT_CLEAR_RAM | - VB_INIT_OUT_ENABLE_DISPLAY | - VB_INIT_OUT_ENABLE_USB_STORAGE | - VB_INIT_OUT_ENABLE_DEVELOPER | - VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); - TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags"); +static void VbInitTestTPM(void) +{ + uint32_t u; + + /* Rollback setup needs to reboot */ + ResetMocks(); + mock_rfs_retval = TPM_E_MUST_REBOOT; + TestVbInit(VBERROR_TPM_REBOOT_REQUIRED, 0, + "Rollback TPM reboot (rec=0)"); + ResetMocks(); + mock_rfs_retval = TPM_E_MUST_REBOOT; + iparams.flags = VB_INIT_FLAG_REC_BUTTON_PRESSED; + TestVbInit(VBERROR_TPM_REBOOT_REQUIRED, VBNV_RECOVERY_RO_TPM_REBOOT, + "Rollback TPM reboot, in recovery, first time"); + /* Ignore if we already tried rebooting */ + ResetMocks(); + mock_rfs_retval = TPM_E_MUST_REBOOT; + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_REBOOT); + VbNvTeardown(&vnc); + TestVbInit(0, 0, "Rollback TPM reboot, in recovery, already retried"); + TEST_EQ(shared->fw_version_tpm, 0x10001, " shared fw_version_tpm"); + + /* Other rollback setup errors */ + ResetMocks(); + mock_rfs_retval = TPM_E_IOERROR; + mock_tpm_version = 0x20002; + TestVbInit(VBERROR_TPM_FIRMWARE_SETUP, VBNV_RECOVERY_RO_TPM_S_ERROR, + "Rollback TPM setup error - not in recovery"); + TEST_EQ(shared->fw_version_tpm, 0, " shared fw_version_tpm not set"); + ResetMocks(); + mock_rfs_retval = TPM_E_IOERROR; + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_US_TEST); + VbNvTeardown(&vnc); + TestVbInit(0, 0, "Rollback TPM setup error ignored in recovery"); + TEST_EQ(shared->fw_version_tpm, 0x10001, " shared fw_version_tpm"); + + /* Virtual developer switch, but not enabled. */ + ResetMocks(); + VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 1); + VbNvTeardown(&vnc); + iparams.flags = VB_INIT_FLAG_VIRTUAL_DEV_SWITCH; + TestVbInit(0, 0, "TPM Dev mode off"); + TEST_EQ(shared->recovery_reason, 0, " recovery reason"); + TEST_EQ(iparams.out_flags, 0, " out flags"); + TEST_EQ(shared->flags, VBSD_HONOR_VIRT_DEV_SWITCH, " shared flags"); + VbNvGet(&vnc, VBNV_DISABLE_DEV_REQUEST, &u); + TEST_EQ(u, 0, " disable dev request"); + + /* Virtual developer switch, enabled. */ + ResetMocks(); + VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 1); + VbNvTeardown(&vnc); + iparams.flags = VB_INIT_FLAG_VIRTUAL_DEV_SWITCH; + mock_virt_dev_sw = 1; + TestVbInit(0, 0, "TPM Dev mode on"); + TEST_EQ(shared->recovery_reason, 0, " recovery reason"); + TEST_EQ(iparams.out_flags, + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE | + VB_INIT_OUT_ENABLE_DEVELOPER | + VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); + TEST_EQ(shared->flags, + VBSD_BOOT_DEV_SWITCH_ON | VBSD_HONOR_VIRT_DEV_SWITCH, + " shared flags"); + /* Disable-request doesn't get cleared because dev mode is still on */ + VbNvGet(&vnc, VBNV_DISABLE_DEV_REQUEST, &u); + TEST_EQ(u, 1, " disable dev request"); + /* Disable request was passed on to RollbackFirmwareSetup() */ + TEST_EQ(rfs_disable_dev_request, 1, " rfs disable dev"); + + /* Ignore virtual developer switch, even though enabled. */ + ResetMocks(); + mock_virt_dev_sw = 1; + TestVbInit(0, 0, "TPM Dev mode on but ignored"); + TEST_EQ(shared->recovery_reason, 0, " recovery reason"); + TEST_EQ(iparams.out_flags, 0, " out flags"); + TEST_EQ(shared->flags, 0, " shared flags"); + + /* HW dev switch on, no virtual developer switch */ + ResetMocks(); + iparams.flags = VB_INIT_FLAG_DEV_SWITCH_ON; + TestVbInit(0, 0, "HW Dev mode on"); + TEST_EQ(shared->recovery_reason, 0, " recovery reason"); + TEST_EQ(iparams.out_flags, + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE | + VB_INIT_OUT_ENABLE_DEVELOPER | + VB_INIT_OUT_ENABLE_ALTERNATE_OS, " out flags"); + TEST_EQ(shared->flags, VBSD_BOOT_DEV_SWITCH_ON, " shared flags"); + + /* Check TPM owner clear request */ + ResetMocks(); + VbNvSet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST, 1); + VbNvTeardown(&vnc); + TestVbInit(0, 0, "TPM clear owner"); + VbNvGet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST, &u); + TEST_EQ(u, 0, " tpm clear request"); + VbNvGet(&vnc, VBNV_CLEAR_TPM_OWNER_DONE, &u); + TEST_EQ(u, 1, " tpm clear request"); + TEST_EQ(rfs_clear_tpm_request, 1, "rfs tpm clear request"); } - /* disable MSVC warnings on unused arguments */ __pragma(warning (disable: 4100)) -int main(int argc, char* argv[]) { - int error_code = 0; - - VbInitTest(); - VbInitTestTPM(); - - if (!gTestSuccess) - error_code = 255; +int main(int argc, char *argv[]) +{ + VbInitTest(); + VbInitTestTPM(); - return error_code; + return gTestSuccess ? 0 : 255; } diff --git a/tests/vboot_common2_tests.c b/tests/vboot_common2_tests.c index 54a3f316..f2ed5260 100644 --- a/tests/vboot_common2_tests.c +++ b/tests/vboot_common2_tests.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. * @@ -7,6 +7,7 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include "cryptolib.h" #include "file_keys.h" @@ -14,231 +15,261 @@ #include "test_common.h" #include "vboot_common.h" - -static void VerifyPublicKeyToRSA(const VbPublicKey* orig_key) { - - RSAPublicKey *rsa; - VbPublicKey *key = PublicKeyAlloc(orig_key->key_size, 0, 0); - - PublicKeyCopy(key, orig_key); - key->algorithm = kNumAlgorithms; - TEST_EQ((size_t)PublicKeyToRSA(key), 0, - "PublicKeyToRSA() invalid algorithm"); - - PublicKeyCopy(key, orig_key); - key->key_size -= 1; - TEST_EQ((size_t)PublicKeyToRSA(key), 0, - "PublicKeyToRSA() invalid size"); - - rsa = PublicKeyToRSA(orig_key); - TEST_NEQ((size_t)rsa, 0, "PublicKeyToRSA() ok"); - if (rsa) { - TEST_EQ((int)rsa->algorithm, (int)key->algorithm, - "PublicKeyToRSA() algorithm"); - RSAPublicKeyFree(rsa); - } +static void VerifyPublicKeyToRSA(const VbPublicKey *orig_key) +{ + RSAPublicKey *rsa; + VbPublicKey *key = PublicKeyAlloc(orig_key->key_size, 0, 0); + + PublicKeyCopy(key, orig_key); + key->algorithm = kNumAlgorithms; + TEST_EQ((size_t)PublicKeyToRSA(key), 0, + "PublicKeyToRSA() invalid algorithm"); + + PublicKeyCopy(key, orig_key); + key->key_size -= 1; + TEST_EQ((size_t)PublicKeyToRSA(key), 0, + "PublicKeyToRSA() invalid size"); + + rsa = PublicKeyToRSA(orig_key); + TEST_NEQ((size_t)rsa, 0, "PublicKeyToRSA() ok"); + if (rsa) { + TEST_EQ((int)rsa->algorithm, (int)key->algorithm, + "PublicKeyToRSA() algorithm"); + RSAPublicKeyFree(rsa); + } } +static void VerifyDataTest(const VbPublicKey *public_key, + const VbPrivateKey *private_key) +{ + const uint8_t test_data[] = "This is some test data to sign."; + const uint64_t test_size = sizeof(test_data); + VbSignature *sig; + RSAPublicKey *rsa; -static void VerifyDataTest(const VbPublicKey* public_key, - const VbPrivateKey* private_key) { + sig = CalculateSignature(test_data, test_size, private_key); + TEST_PTR_NEQ(sig, 0, "VerifyData() calculate signature"); - const uint8_t test_data[] = "This is some test data to sign."; - const uint64_t test_size = sizeof(test_data); - VbSignature* sig; - RSAPublicKey* rsa; + rsa = PublicKeyToRSA(public_key); + TEST_PTR_NEQ(rsa, 0, "VerifyData() calculate rsa"); - sig = CalculateSignature(test_data, test_size, private_key); - rsa = PublicKeyToRSA(public_key); - TEST_NEQ(sig && rsa, 0, "VerifyData() prerequisites"); - if (!sig || !rsa) - return; + if (!sig || !rsa) + return; - TEST_EQ(VerifyData(test_data, test_size, sig, rsa), 0, "VerifyData() ok"); + TEST_EQ(VerifyData(test_data, test_size, sig, rsa), 0, + "VerifyData() ok"); - sig->sig_size -= 16; - TEST_EQ(VerifyData(test_data, test_size, sig, rsa), 1, - "VerifyData() wrong sig size"); - sig->sig_size += 16; + sig->sig_size -= 16; + TEST_EQ(VerifyData(test_data, test_size, sig, rsa), 1, + "VerifyData() wrong sig size"); + sig->sig_size += 16; - TEST_EQ(VerifyData(test_data, test_size - 1, sig, rsa), 1, - "VerifyData() input buffer too small"); + TEST_EQ(VerifyData(test_data, test_size - 1, sig, rsa), 1, + "VerifyData() input buffer too small"); - GetSignatureData(sig)[0] ^= 0x5A; - TEST_EQ(VerifyData(test_data, test_size, sig, rsa), 1, - "VerifyData() wrong sig"); + GetSignatureData(sig)[0] ^= 0x5A; + TEST_EQ(VerifyData(test_data, test_size, sig, rsa), 1, + "VerifyData() wrong sig"); - RSAPublicKeyFree(rsa); - free(sig); + RSAPublicKeyFree(rsa); + free(sig); } - -static void VerifyDigestTest(const VbPublicKey* public_key, - const VbPrivateKey* private_key) { - - const uint8_t test_data[] = "This is some other test data to sign."; - VbSignature* sig; - RSAPublicKey* rsa; - uint8_t* digest; - - sig = CalculateSignature(test_data, sizeof(test_data), private_key); - rsa = PublicKeyToRSA(public_key); - digest = DigestBuf(test_data, sizeof(test_data), (int)public_key->algorithm); - TEST_NEQ(sig && rsa && digest, 0, "VerifyData() prerequisites"); - if (!sig || !rsa || !digest) - return; - - TEST_EQ(VerifyDigest(digest, sig, rsa), 0, "VerifyDigest() ok"); - - GetSignatureData(sig)[0] ^= 0x5A; - TEST_EQ(VerifyDigest(digest, sig, rsa), 1, "VerifyDigest() wrong sig"); - - RSAPublicKeyFree(rsa); - free(sig); - free(digest); +static void VerifyDigestTest(const VbPublicKey *public_key, + const VbPrivateKey *private_key) +{ + const uint8_t test_data[] = "This is some other test data to sign."; + VbSignature *sig; + RSAPublicKey *rsa; + uint8_t *digest; + + sig = CalculateSignature(test_data, sizeof(test_data), private_key); + rsa = PublicKeyToRSA(public_key); + digest = DigestBuf(test_data, sizeof(test_data), + (int)public_key->algorithm); + TEST_NEQ(sig && rsa && digest, 0, "VerifyData() prerequisites"); + if (!sig || !rsa || !digest) + return; + + TEST_EQ(VerifyDigest(digest, sig, rsa), 0, "VerifyDigest() ok"); + + GetSignatureData(sig)[0] ^= 0x5A; + TEST_EQ(VerifyDigest(digest, sig, rsa), 1, "VerifyDigest() wrong sig"); + + RSAPublicKeyFree(rsa); + free(sig); + free(digest); } - static void ReSignKernelPreamble(VbKernelPreambleHeader *h, - const VbPrivateKey *key) { - VbSignature *sig = CalculateSignature((const uint8_t*)h, - h->preamble_signature.data_size, key); + const VbPrivateKey *key) +{ + VbSignature *sig = CalculateSignature((const uint8_t *)h, + h->preamble_signature.data_size, key); - SignatureCopy(&h->preamble_signature, sig); - free(sig); + SignatureCopy(&h->preamble_signature, sig); + free(sig); } - -static void VerifyKernelPreambleTest(const VbPublicKey* public_key, - const VbPrivateKey* private_key) { - - VbKernelPreambleHeader *hdr; - VbKernelPreambleHeader *h; - RSAPublicKey* rsa; - unsigned hsize; - - /* Create a dummy signature */ - VbSignature *body_sig = SignatureAlloc(56, 78); - - rsa = PublicKeyToRSA(public_key); - hdr = CreateKernelPreamble(0x1234, 0x100000, 0x300000, 0x4000, body_sig, - 0, private_key); - TEST_NEQ(hdr && rsa, 0, "VerifyKernelPreamble() prerequisites"); - if (!hdr) - return; - hsize = (unsigned) hdr->preamble_size; - h = (VbKernelPreambleHeader*)malloc(hsize + 16384); - - TEST_EQ(VerifyKernelPreamble(hdr, hsize, rsa), 0, - "VerifyKernelPreamble() ok using key"); - TEST_NEQ(VerifyKernelPreamble(hdr, hsize - 1, rsa), 0, - "VerifyKernelPreamble() size--"); - TEST_EQ(VerifyKernelPreamble(hdr, hsize + 1, rsa), 0, - "VerifyKernelPreamble() size++"); - - /* Care about major version but not minor */ - Memcpy(h, hdr, hsize); - h->header_version_major++; - ReSignKernelPreamble(h, private_key); - TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, - "VerifyKernelPreamble() major++"); - - Memcpy(h, hdr, hsize); - h->header_version_major--; - ReSignKernelPreamble(h, private_key); - TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, - "VerifyKernelPreamble() major--"); - - Memcpy(h, hdr, hsize); - h->header_version_minor++; - ReSignKernelPreamble(h, private_key); - TEST_EQ(VerifyKernelPreamble(h, hsize, rsa), 0, - "VerifyKernelPreamble() minor++"); - - Memcpy(h, hdr, hsize); - h->header_version_minor--; - ReSignKernelPreamble(h, private_key); - TEST_EQ(VerifyKernelPreamble(h, hsize, rsa), 0, - "VerifyKernelPreamble() minor--"); - - /* Check signature */ - Memcpy(h, hdr, hsize); - h->preamble_signature.sig_offset = hsize; - ReSignKernelPreamble(h, private_key); - TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, - "VerifyKernelPreamble() sig off end"); - - Memcpy(h, hdr, hsize); - h->preamble_signature.sig_size--; - ReSignKernelPreamble(h, private_key); - TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, - "VerifyKernelPreamble() sig too small"); - - Memcpy(h, hdr, hsize); - GetSignatureData(&h->body_signature)[0] ^= 0x34; - TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, - "VerifyKernelPreamble() sig mismatch"); - - /* Check that we signed header and body sig */ - Memcpy(h, hdr, hsize); - h->preamble_signature.data_size = 4; - h->body_signature.sig_offset = 0; - h->body_signature.sig_size = 0; - ReSignKernelPreamble(h, private_key); - TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, - "VerifyKernelPreamble() didn't sign header"); - - Memcpy(h, hdr, hsize); - h->body_signature.sig_offset = hsize; - ReSignKernelPreamble(h, private_key); - TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, - "VerifyKernelPreamble() body sig off end"); - - /* TODO: verify parser can support a bigger header. */ - - free(h); - RSAPublicKeyFree(rsa); - free(hdr); +static void VerifyKernelPreambleTest(const VbPublicKey *public_key, + const VbPrivateKey *private_key) +{ + VbKernelPreambleHeader *hdr; + VbKernelPreambleHeader *h; + RSAPublicKey *rsa; + unsigned hsize; + + /* Create a dummy signature */ + VbSignature *body_sig = SignatureAlloc(56, 78); + + rsa = PublicKeyToRSA(public_key); + hdr = CreateKernelPreamble(0x1234, 0x100000, 0x300000, 0x4000, body_sig, + 0, private_key); + TEST_NEQ(hdr && rsa, 0, "VerifyKernelPreamble() prerequisites"); + if (!hdr) + return; + hsize = (unsigned) hdr->preamble_size; + h = (VbKernelPreambleHeader *)malloc(hsize + 16384); + + TEST_EQ(VerifyKernelPreamble(hdr, hsize, rsa), 0, + "VerifyKernelPreamble() ok using key"); + TEST_NEQ(VerifyKernelPreamble(hdr, hsize - 1, rsa), 0, + "VerifyKernelPreamble() size--"); + TEST_EQ(VerifyKernelPreamble(hdr, hsize + 1, rsa), 0, + "VerifyKernelPreamble() size++"); + + /* Care about major version but not minor */ + Memcpy(h, hdr, hsize); + h->header_version_major++; + ReSignKernelPreamble(h, private_key); + TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, + "VerifyKernelPreamble() major++"); + + Memcpy(h, hdr, hsize); + h->header_version_major--; + ReSignKernelPreamble(h, private_key); + TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, + "VerifyKernelPreamble() major--"); + + Memcpy(h, hdr, hsize); + h->header_version_minor++; + ReSignKernelPreamble(h, private_key); + TEST_EQ(VerifyKernelPreamble(h, hsize, rsa), 0, + "VerifyKernelPreamble() minor++"); + + Memcpy(h, hdr, hsize); + h->header_version_minor--; + ReSignKernelPreamble(h, private_key); + TEST_EQ(VerifyKernelPreamble(h, hsize, rsa), 0, + "VerifyKernelPreamble() minor--"); + + /* Check signature */ + Memcpy(h, hdr, hsize); + h->preamble_signature.sig_offset = hsize; + ReSignKernelPreamble(h, private_key); + TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, + "VerifyKernelPreamble() sig off end"); + + Memcpy(h, hdr, hsize); + h->preamble_signature.sig_size--; + ReSignKernelPreamble(h, private_key); + TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, + "VerifyKernelPreamble() sig too small"); + + Memcpy(h, hdr, hsize); + GetSignatureData(&h->body_signature)[0] ^= 0x34; + TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, + "VerifyKernelPreamble() sig mismatch"); + + /* Check that we signed header and body sig */ + Memcpy(h, hdr, hsize); + h->preamble_signature.data_size = 4; + h->body_signature.sig_offset = 0; + h->body_signature.sig_size = 0; + ReSignKernelPreamble(h, private_key); + TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, + "VerifyKernelPreamble() didn't sign header"); + + Memcpy(h, hdr, hsize); + h->body_signature.sig_offset = hsize; + ReSignKernelPreamble(h, private_key); + TEST_NEQ(VerifyKernelPreamble(h, hsize, rsa), 0, + "VerifyKernelPreamble() body sig off end"); + + /* TODO: verify parser can support a bigger header. */ + + free(h); + RSAPublicKeyFree(rsa); + free(hdr); } +int test_algorithm(int key_algorithm, const char *keys_dir) +{ + char filename[1024]; + int rsa_len = siglen_map[key_algorithm] * 8; + + VbPrivateKey *private_key = NULL; + VbPublicKey *public_key = NULL; + + printf("***Testing algorithm: %s\n", algo_strings[key_algorithm]); + + sprintf(filename, "%s/key_rsa%d.pem", keys_dir, rsa_len); + private_key = PrivateKeyReadPem(filename, key_algorithm); + if (!private_key) { + fprintf(stderr, "Error reading private_key: %s\n", filename); + return 1; + } + + sprintf(filename, "%s/key_rsa%d.keyb", keys_dir, rsa_len); + public_key = PublicKeyReadKeyb(filename, key_algorithm, 1); + if (!public_key) { + fprintf(stderr, "Error reading public_key: %s\n", filename); + return 1; + } + + VerifyPublicKeyToRSA(public_key); + VerifyDataTest(public_key, private_key); + VerifyDigestTest(public_key, private_key); + VerifyKernelPreambleTest(public_key, private_key); + + if (public_key) + free(public_key); + if (private_key) + free(private_key); + + return 0; +} -int main(int argc, char* argv[]) { - VbPrivateKey* private_key = NULL; - VbPublicKey* public_key = NULL; - int key_algorithm; - - int error_code = 0; - - if(argc != 4) { - fprintf(stderr, "Usage: %s <key_algorithm> <key> <processed pubkey>" - " <signing key> <processed signing key>\n", argv[0]); - return -1; - } +/* + * Test only the algorithms we use: + * 4 (rsa2048 sha256) + * 7 (rsa4096 sha256) + * 11 (rsa8192 sha512) + */ +const int key_algs[] = {4, 7, 11}; - /* Read verification keys and create a test image. */ - key_algorithm = atoi(argv[1]); +int main(int argc, char *argv[]) { + if (argc == 2) { + int i; - private_key = PrivateKeyReadPem(argv[2], key_algorithm); - if (!private_key) { - fprintf(stderr, "Error reading private_key"); - return 1; - } + for (i = 0; i < ARRAY_SIZE(key_algs); i++) { + if (test_algorithm(key_algs[i], argv[1])) + return 1; + } - public_key = PublicKeyReadKeyb(argv[3], key_algorithm, 1); - if (!public_key) { - fprintf(stderr, "Error reading public_key"); - return 1; - } + } else if (argc == 3 && !strcasecmp(argv[2], "--all")) { + /* Test all the algorithms */ + int alg; - VerifyPublicKeyToRSA(public_key); - VerifyDataTest(public_key, private_key); - VerifyDigestTest(public_key, private_key); - VerifyKernelPreambleTest(public_key, private_key); + for (alg = 0; alg < kNumAlgorithms; alg++) { + if (test_algorithm(alg, argv[1])) + return 1; + } - if (public_key) - free(public_key); - if (private_key) - free(private_key); + } else { + fprintf(stderr, "Usage: %s <keys_dir> [--all]", argv[0]); + return -1; + } - return error_code; + return gTestSuccess ? 0 : 255; } diff --git a/tests/vboot_common3_tests.c b/tests/vboot_common3_tests.c index a63e477c..ce818a4c 100644 --- a/tests/vboot_common3_tests.c +++ b/tests/vboot_common3_tests.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. * @@ -7,6 +7,7 @@ #include <stdio.h> #include <stdlib.h> +#include <string.h> #include "cryptolib.h" #include "file_keys.h" @@ -254,56 +255,107 @@ static void VerifyFirmwarePreambleTest(const VbPublicKey* public_key, free(hdr); } +int test_permutation(int signing_key_algorithm, int data_key_algorithm, + const char *keys_dir) +{ + char filename[1024]; + int signing_rsa_len = siglen_map[signing_key_algorithm] * 8;; + int data_rsa_len = siglen_map[data_key_algorithm] * 8;; + + VbPrivateKey* signing_private_key = NULL; + VbPublicKey* signing_public_key = NULL; + VbPublicKey* data_public_key = NULL; + + printf("***Testing signing algorithm: %s\n", + algo_strings[signing_key_algorithm]); + printf("***With data key algorithm: %s\n", + algo_strings[data_key_algorithm]); + + sprintf(filename, "%s/key_rsa%d.pem", keys_dir, signing_rsa_len); + signing_private_key = PrivateKeyReadPem(filename, + signing_key_algorithm); + if (!signing_private_key) { + fprintf(stderr, "Error reading signing_private_key: %s\n", + filename); + return 1; + } + + sprintf(filename, "%s/key_rsa%d.keyb", keys_dir, signing_rsa_len); + signing_public_key = PublicKeyReadKeyb(filename, + signing_key_algorithm, 1); + if (!signing_public_key) { + fprintf(stderr, "Error reading signing_public_key: %s\n", + filename); + return 1; + } + + sprintf(filename, "%s/key_rsa%d.keyb", keys_dir, data_rsa_len); + data_public_key = PublicKeyReadKeyb(filename, + data_key_algorithm, 1); + if (!data_public_key) { + fprintf(stderr, "Error reading data_public_key: %s\n", + filename); + return 1; + } + + KeyBlockVerifyTest(signing_public_key, signing_private_key, + data_public_key); + VerifyFirmwarePreambleTest(signing_public_key, signing_private_key, + data_public_key); + + if (signing_public_key) + free(signing_public_key); + if (signing_private_key) + free(signing_private_key); + if (data_public_key) + free(data_public_key); + + return 0; +} -int main(int argc, char* argv[]) { - VbPrivateKey* signing_private_key = NULL; - VbPublicKey* signing_public_key = NULL; - int signing_key_algorithm; - - VbPublicKey* data_public_key = NULL; - int data_key_algorithm; - - int error_code = 0; - - if(argc != 7) { - fprintf(stderr, "Usage: %s <signing_key_algorithm> <data_key_algorithm>" - " <signing key> <processed signing pubkey>" - " <data key> <processed data pubkey>\n", argv[0]); - return -1; - } - - /* Read verification keys and create a test image. */ - signing_key_algorithm = atoi(argv[1]); - data_key_algorithm = atoi(argv[2]); - - signing_private_key = PrivateKeyReadPem(argv[3], signing_key_algorithm); - if (!signing_private_key) { - fprintf(stderr, "Error reading signing_private_key"); - return 1; - } - - signing_public_key = PublicKeyReadKeyb(argv[4], signing_key_algorithm, 1); - if (!signing_public_key) { - fprintf(stderr, "Error reading signing_public_key"); - return 1; - } - - data_public_key = PublicKeyReadKeyb(argv[6], data_key_algorithm, 1); - if (!data_public_key) { - fprintf(stderr, "Error reading data_public_key"); - return 1; - } - - KeyBlockVerifyTest(signing_public_key, signing_private_key, data_public_key); - VerifyFirmwarePreambleTest(signing_public_key, signing_private_key, - data_public_key); - - if (signing_public_key) - free(signing_public_key); - if (signing_private_key) - free(signing_private_key); - if (data_public_key) - free(data_public_key); - - return error_code; +struct test_perm +{ + int signing_algorithm; + int data_key_algorithm; +}; + +/* + * Permutations of signing and data key algorithms in active use: + * 7 (rsa4096 sha256) - 4 (rsa2048 sha256) + * 11 (rsa8192 sha512) - 4 (rsa2048 sha256) + * 11 (rsa8192 sha512) - 7 (rsa4096 sha256) + */ +const struct test_perm test_perms[] = {{7, 4}, {11, 4}, {11, 7}}; + +int main(int argc, char* argv[]) +{ + if (argc == 2) { + /* Test only the algorithms we use */ + int i; + + for (i = 0; i < ARRAY_SIZE(test_perms); i++) { + if (test_permutation(test_perms[i].signing_algorithm, + test_perms[i].data_key_algorithm, + argv[1])) + return 1; + } + + } else if (argc == 3 && !strcasecmp(argv[2], "--all")) { + /* Test all the algorithms */ + int sign_alg, data_alg; + + for (sign_alg = 0; sign_alg < kNumAlgorithms; sign_alg++) { + for (data_alg = 0; data_alg < kNumAlgorithms; + data_alg++) { + if (test_permutation(sign_alg, data_alg, + argv[1])) + return 1; + } + } + } else { + fprintf(stderr, "Usage: %s <keys_dir> [--all]", argv[0]); + return -1; + } + + return gTestSuccess ? 0 : 255; } diff --git a/tests/vboot_common_tests.c b/tests/vboot_common_tests.c index 85b2d4ed..f36ec76d 100644 --- a/tests/vboot_common_tests.c +++ b/tests/vboot_common_tests.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. * @@ -12,209 +12,220 @@ #include "utility.h" #include "vboot_common.h" -/* Test struct packing for vboot_struct.h structs which are passed - * between firmware and OS, or passed between different phases of - * firmware. */ -static void StructPackingTest(void) { - TEST_EQ(EXPECTED_VBPUBLICKEY_SIZE, sizeof(VbPublicKey), - "sizeof(VbPublicKey)"); - TEST_EQ(EXPECTED_VBSIGNATURE_SIZE, sizeof(VbSignature), - "sizeof(VbSignature)"); - TEST_EQ(EXPECTED_VBKEYBLOCKHEADER_SIZE, sizeof(VbKeyBlockHeader), - "sizeof(VbKeyBlockHeader)"); - TEST_EQ(EXPECTED_VBFIRMWAREPREAMBLEHEADER2_0_SIZE, - sizeof(VbFirmwarePreambleHeader2_0), - "sizeof(VbFirmwarePreambleHeader2_0)"); - TEST_EQ(EXPECTED_VBFIRMWAREPREAMBLEHEADER2_1_SIZE, - sizeof(VbFirmwarePreambleHeader), - "sizeof(VbFirmwarePreambleHeader)"); - TEST_EQ(EXPECTED_VBKERNELPREAMBLEHEADER_SIZE, - sizeof(VbKernelPreambleHeader), "sizeof(VbKernelPreambleHeader)"); - - TEST_EQ(VB_SHARED_DATA_HEADER_SIZE_V1, - (long)&((VbSharedDataHeader*)NULL)->recovery_reason, - "sizeof(VbSharedDataHeader) V1"); - - TEST_EQ(VB_SHARED_DATA_HEADER_SIZE_V2, - sizeof(VbSharedDataHeader), - "sizeof(VbSharedDataHeader) V2"); +/* + * Test struct packing for vboot_struct.h structs which are passed between + * firmware and OS, or passed between different phases of firmware. + */ +static void StructPackingTest(void) +{ + TEST_EQ(EXPECTED_VBPUBLICKEY_SIZE, sizeof(VbPublicKey), + "sizeof(VbPublicKey)"); + TEST_EQ(EXPECTED_VBSIGNATURE_SIZE, sizeof(VbSignature), + "sizeof(VbSignature)"); + TEST_EQ(EXPECTED_VBKEYBLOCKHEADER_SIZE, sizeof(VbKeyBlockHeader), + "sizeof(VbKeyBlockHeader)"); + TEST_EQ(EXPECTED_VBFIRMWAREPREAMBLEHEADER2_0_SIZE, + sizeof(VbFirmwarePreambleHeader2_0), + "sizeof(VbFirmwarePreambleHeader2_0)"); + TEST_EQ(EXPECTED_VBFIRMWAREPREAMBLEHEADER2_1_SIZE, + sizeof(VbFirmwarePreambleHeader), + "sizeof(VbFirmwarePreambleHeader)"); + TEST_EQ(EXPECTED_VBKERNELPREAMBLEHEADER_SIZE, + sizeof(VbKernelPreambleHeader), + "sizeof(VbKernelPreambleHeader)"); + + TEST_EQ(VB_SHARED_DATA_HEADER_SIZE_V1, + (long)&((VbSharedDataHeader*)NULL)->recovery_reason, + "sizeof(VbSharedDataHeader) V1"); + + TEST_EQ(VB_SHARED_DATA_HEADER_SIZE_V2, + sizeof(VbSharedDataHeader), + "sizeof(VbSharedDataHeader) V2"); } - /* Test array size macro */ -static void ArraySizeTest(void) { - uint8_t arr1[12]; - uint32_t arr2[7]; - uint64_t arr3[9]; - - TEST_EQ(ARRAY_SIZE(arr1), 12, "ARRAYSIZE(uint8_t)"); - TEST_EQ(ARRAY_SIZE(arr2), 7, "ARRAYSIZE(uint32_t)"); - TEST_EQ(ARRAY_SIZE(arr3), 9, "ARRAYSIZE(uint64_t)"); +static void ArraySizeTest(void) +{ + uint8_t arr1[12]; + uint32_t arr2[7]; + uint64_t arr3[9]; + + TEST_EQ(ARRAY_SIZE(arr1), 12, "ARRAYSIZE(uint8_t)"); + TEST_EQ(ARRAY_SIZE(arr2), 7, "ARRAYSIZE(uint32_t)"); + TEST_EQ(ARRAY_SIZE(arr3), 9, "ARRAYSIZE(uint64_t)"); } - /* Helper functions not dependent on specific key sizes */ -static void VerifyHelperFunctions(void) { - - { - uint8_t *p = (uint8_t *)VerifyHelperFunctions; - TEST_EQ((int)OffsetOf(p, p), 0, "OffsetOf() equal"); - TEST_EQ((int)OffsetOf(p, p+10), 10, "OffsetOf() positive"); - TEST_EQ((int)OffsetOf(p, p+0x12345678), 0x12345678, "OffsetOf() large"); - } - - { - VbPublicKey k = {sizeof(k), 2, 3, 4}; - TEST_EQ((int)OffsetOf(&k, GetPublicKeyData(&k)), sizeof(k), - "GetPublicKeyData() adjacent"); - TEST_EQ((int)OffsetOf(&k, GetPublicKeyDataC(&k)), sizeof(k), - "GetPublicKeyDataC() adjacent"); - } - - { - VbPublicKey k = {123, 2, 3, 4}; - TEST_EQ((int)OffsetOf(&k, GetPublicKeyData(&k)), 123, - "GetPublicKeyData() spaced"); - TEST_EQ((int)OffsetOf(&k, GetPublicKeyDataC(&k)), 123, - "GetPublicKeyDataC() spaced"); - } - - { - uint8_t *p = (uint8_t *)VerifyHelperFunctions; - TEST_EQ(VerifyMemberInside(p, 20, p, 6, 11, 3), 0, "MemberInside ok 1"); - TEST_EQ(VerifyMemberInside(p, 20, p+4, 4, 8, 4), 0, "MemberInside ok 2"); - TEST_EQ(VerifyMemberInside(p, 20, p-4, 4, 8, 4), 1, - "MemberInside member before parent"); - TEST_EQ(VerifyMemberInside(p, 20, p+20, 4, 8, 4), 1, - "MemberInside member after parent"); - TEST_EQ(VerifyMemberInside(p, 20, p, 21, 0, 0), 1, - "MemberInside member too big"); - TEST_EQ(VerifyMemberInside(p, 20, p, 4, 21, 0), 1, - "MemberInside data after parent"); - TEST_EQ(VerifyMemberInside(p, 20, p, 4, (uint64_t)-1, 0), 1, - "MemberInside data before parent"); - TEST_EQ(VerifyMemberInside(p, 20, p, 4, 4, 17), 1, - "MemberInside data too big"); - } - - { - VbPublicKey k = {sizeof(k), 128, 0, 0}; - TEST_EQ(VerifyPublicKeyInside(&k, sizeof(k)+128, &k), 0, - "PublicKeyInside ok 1"); - TEST_EQ(VerifyPublicKeyInside(&k - 1, 2*sizeof(k)+128, &k), 0, - "PublicKeyInside ok 2"); - TEST_EQ(VerifyPublicKeyInside(&k, 128, &k), 1, - "PublicKeyInside key too big"); - } - { - VbPublicKey k = {100, 4, 0, 0}; - TEST_EQ(VerifyPublicKeyInside(&k, 99, &k), 1, - "PublicKeyInside offset too big"); - } - { - VbSignature s = {sizeof(s), 128, 2000}; - TEST_EQ(VerifySignatureInside(&s, sizeof(s)+128, &s), 0, - "SignatureInside ok 1"); - TEST_EQ(VerifySignatureInside(&s - 1, 2*sizeof(s)+128, &s), 0, - "SignatureInside ok 2"); - TEST_EQ(VerifySignatureInside(&s, 128, &s), 1, - "SignatureInside sig too big"); - } - { - VbSignature s = {100, 4, 0}; - TEST_EQ(VerifySignatureInside(&s, 99, &s), 1, - "SignatureInside offset too big"); - } +static void VerifyHelperFunctions(void) +{ + { + uint8_t *p = (uint8_t *)VerifyHelperFunctions; + TEST_EQ((int)OffsetOf(p, p), 0, "OffsetOf() equal"); + TEST_EQ((int)OffsetOf(p, p+10), 10, "OffsetOf() positive"); + TEST_EQ((int)OffsetOf(p, p+0x12345678), 0x12345678, + "OffsetOf() large"); + } + + { + VbPublicKey k = {sizeof(k), 2, 3, 4}; + TEST_EQ((int)OffsetOf(&k, GetPublicKeyData(&k)), sizeof(k), + "GetPublicKeyData() adjacent"); + TEST_EQ((int)OffsetOf(&k, GetPublicKeyDataC(&k)), sizeof(k), + "GetPublicKeyDataC() adjacent"); + } + + { + VbPublicKey k = {123, 2, 3, 4}; + TEST_EQ((int)OffsetOf(&k, GetPublicKeyData(&k)), 123, + "GetPublicKeyData() spaced"); + TEST_EQ((int)OffsetOf(&k, GetPublicKeyDataC(&k)), 123, + "GetPublicKeyDataC() spaced"); + } + + { + uint8_t *p = (uint8_t *)VerifyHelperFunctions; + TEST_EQ(VerifyMemberInside(p, 20, p, 6, 11, 3), 0, + "MemberInside ok 1"); + TEST_EQ(VerifyMemberInside(p, 20, p+4, 4, 8, 4), 0, + "MemberInside ok 2"); + TEST_EQ(VerifyMemberInside(p, 20, p-4, 4, 8, 4), 1, + "MemberInside member before parent"); + TEST_EQ(VerifyMemberInside(p, 20, p+20, 4, 8, 4), 1, + "MemberInside member after parent"); + TEST_EQ(VerifyMemberInside(p, 20, p, 21, 0, 0), 1, + "MemberInside member too big"); + TEST_EQ(VerifyMemberInside(p, 20, p, 4, 21, 0), 1, + "MemberInside data after parent"); + TEST_EQ(VerifyMemberInside(p, 20, p, 4, (uint64_t)-1, 0), 1, + "MemberInside data before parent"); + TEST_EQ(VerifyMemberInside(p, 20, p, 4, 4, 17), 1, + "MemberInside data too big"); + } + + { + VbPublicKey k = {sizeof(k), 128, 0, 0}; + TEST_EQ(VerifyPublicKeyInside(&k, sizeof(k)+128, &k), 0, + "PublicKeyInside ok 1"); + TEST_EQ(VerifyPublicKeyInside(&k - 1, 2*sizeof(k)+128, &k), 0, + "PublicKeyInside ok 2"); + TEST_EQ(VerifyPublicKeyInside(&k, 128, &k), 1, + "PublicKeyInside key too big"); + } + + { + VbPublicKey k = {100, 4, 0, 0}; + TEST_EQ(VerifyPublicKeyInside(&k, 99, &k), 1, + "PublicKeyInside offset too big"); + } + + { + VbSignature s = {sizeof(s), 128, 2000}; + TEST_EQ(VerifySignatureInside(&s, sizeof(s)+128, &s), 0, + "SignatureInside ok 1"); + TEST_EQ(VerifySignatureInside(&s - 1, 2*sizeof(s)+128, &s), 0, + "SignatureInside ok 2"); + TEST_EQ(VerifySignatureInside(&s, 128, &s), 1, + "SignatureInside sig too big"); + } + + { + VbSignature s = {100, 4, 0}; + TEST_EQ(VerifySignatureInside(&s, 99, &s), 1, + "SignatureInside offset too big"); + } } - /* Public key utility functions */ -static void PublicKeyTest(void) { - VbPublicKey k[3]; - VbPublicKey j[5]; - - /* Fill some bits of the public key data */ - Memset(j, 0, sizeof(j)); - Memset(k, 0x42, sizeof(k)); - k[1].key_size = 12345; - k[2].key_version = 67; - - PublicKeyInit(k, (uint8_t*)(k + 1), 2 * sizeof(VbPublicKey)); - TEST_EQ(k->key_offset, sizeof(VbPublicKey), "PublicKeyInit key_offset"); - TEST_EQ(k->key_size, 2 * sizeof(VbPublicKey), "PublicKeyInit key_size"); - TEST_EQ(k->algorithm, kNumAlgorithms, "PublicKeyInit algorithm"); - TEST_EQ(k->key_version, 0, "PublicKeyInit key_version"); - - /* Set algorithm and version, so we can tell if they get copied */ - k->algorithm = 3; - k->key_version = 21; - - /* Copying to a smaller destination should fail */ - PublicKeyInit(j, (uint8_t*)(j + 1), 2 * sizeof(VbPublicKey) - 1); - TEST_NEQ(0, PublicKeyCopy(j, k), "PublicKeyCopy too small"); - - /* Copying to same or larger size should succeed */ - PublicKeyInit(j, (uint8_t*)(j + 2), 2 * sizeof(VbPublicKey) + 1); - TEST_EQ(0, PublicKeyCopy(j, k), "PublicKeyCopy same"); - /* Offset in destination shouldn't have been modified */ - TEST_EQ(j->key_offset, 2 * sizeof(VbPublicKey), "PublicKeyCopy key_offset"); - /* Size should have been reduced to match the source */ - TEST_EQ(k->key_size, 2 * sizeof(VbPublicKey), "PublicKeyCopy key_size"); - /* Other fields should have been copied */ - TEST_EQ(k->algorithm, j->algorithm, "PublicKeyCopy algorithm"); - TEST_EQ(k->key_version, j->key_version, "PublicKeyCopy key_version"); - /* Data should have been copied */ - TEST_EQ(0, Memcmp(GetPublicKeyData(k), GetPublicKeyData(j), k->key_size), - "PublicKeyCopy data"); +static void PublicKeyTest(void) +{ + VbPublicKey k[3]; + VbPublicKey j[5]; + + /* Fill some bits of the public key data */ + Memset(j, 0, sizeof(j)); + Memset(k, 0x42, sizeof(k)); + k[1].key_size = 12345; + k[2].key_version = 67; + + PublicKeyInit(k, (uint8_t*)(k + 1), 2 * sizeof(VbPublicKey)); + TEST_EQ(k->key_offset, sizeof(VbPublicKey), "PublicKeyInit key_offset"); + TEST_EQ(k->key_size, 2 * sizeof(VbPublicKey), "PublicKeyInit key_size"); + TEST_EQ(k->algorithm, kNumAlgorithms, "PublicKeyInit algorithm"); + TEST_EQ(k->key_version, 0, "PublicKeyInit key_version"); + + /* Set algorithm and version, so we can tell if they get copied */ + k->algorithm = 3; + k->key_version = 21; + + /* Copying to a smaller destination should fail */ + PublicKeyInit(j, (uint8_t*)(j + 1), 2 * sizeof(VbPublicKey) - 1); + TEST_NEQ(0, PublicKeyCopy(j, k), "PublicKeyCopy too small"); + + /* Copying to same or larger size should succeed */ + PublicKeyInit(j, (uint8_t*)(j + 2), 2 * sizeof(VbPublicKey) + 1); + TEST_EQ(0, PublicKeyCopy(j, k), "PublicKeyCopy same"); + /* Offset in destination shouldn't have been modified */ + TEST_EQ(j->key_offset, 2 * sizeof(VbPublicKey), + "PublicKeyCopy key_offset"); + /* Size should have been reduced to match the source */ + TEST_EQ(k->key_size, 2 * sizeof(VbPublicKey), "PublicKeyCopy key_size"); + /* Other fields should have been copied */ + TEST_EQ(k->algorithm, j->algorithm, "PublicKeyCopy algorithm"); + TEST_EQ(k->key_version, j->key_version, "PublicKeyCopy key_version"); + /* Data should have been copied */ + TEST_EQ(0, + Memcmp(GetPublicKeyData(k), GetPublicKeyData(j), k->key_size), + "PublicKeyCopy data"); } - /* VbSharedData utility tests */ -static void VbSharedDataTest(void) { - uint8_t buf[VB_SHARED_DATA_MIN_SIZE + 1]; - VbSharedDataHeader* d = (VbSharedDataHeader*)buf; - - TEST_NEQ(VBOOT_SUCCESS, VbSharedDataInit(d, sizeof(VbSharedDataHeader) - 1), - "VbSharedDataInit too small"); - TEST_NEQ(VBOOT_SUCCESS, VbSharedDataInit(d, VB_SHARED_DATA_MIN_SIZE - 1), - "VbSharedDataInit too small 2"); - TEST_NEQ(VBOOT_SUCCESS, VbSharedDataInit(NULL, VB_SHARED_DATA_MIN_SIZE), - "VbSharedDataInit null"); - - Memset(buf, 0x68, sizeof(buf)); - TEST_EQ(VBOOT_SUCCESS, VbSharedDataInit(d, VB_SHARED_DATA_MIN_SIZE), - "VbSharedDataInit"); - /* Check fields that should have been initialized */ - TEST_EQ(d->magic, VB_SHARED_DATA_MAGIC, "VbSharedDataInit magic"); - TEST_EQ(d->struct_version, VB_SHARED_DATA_VERSION, - "VbSharedDataInit version"); - TEST_EQ(d->struct_size, sizeof(VbSharedDataHeader), - "VbSharedDataInit struct_size"); - TEST_EQ(d->data_size, VB_SHARED_DATA_MIN_SIZE, "VbSharedDataInit data_size"); - TEST_EQ(d->data_used, d->struct_size, "VbSharedDataInit data_used"); - TEST_EQ(d->firmware_index, 0xFF, "VbSharedDataInit firmware index"); - /* Sample some other fields to make sure they were zeroed */ - TEST_EQ(d->flags, 0, "VbSharedDataInit firmware flags"); - TEST_EQ(d->lk_call_count, 0, "VbSharedDataInit lk_call_count"); - TEST_EQ(d->kernel_version_lowest, 0, - "VbSharedDataInit kernel_version_lowest"); +static void VbSharedDataTest(void) +{ + uint8_t buf[VB_SHARED_DATA_MIN_SIZE + 1]; + VbSharedDataHeader* d = (VbSharedDataHeader*)buf; + + TEST_NEQ(VBOOT_SUCCESS, + VbSharedDataInit(d, sizeof(VbSharedDataHeader) - 1), + "VbSharedDataInit too small"); + TEST_NEQ(VBOOT_SUCCESS, + VbSharedDataInit(d, VB_SHARED_DATA_MIN_SIZE - 1), + "VbSharedDataInit too small 2"); + TEST_NEQ(VBOOT_SUCCESS, + VbSharedDataInit(NULL, VB_SHARED_DATA_MIN_SIZE), + "VbSharedDataInit null"); + + Memset(buf, 0x68, sizeof(buf)); + TEST_EQ(VBOOT_SUCCESS, VbSharedDataInit(d, VB_SHARED_DATA_MIN_SIZE), + "VbSharedDataInit"); + + /* Check fields that should have been initialized */ + TEST_EQ(d->magic, VB_SHARED_DATA_MAGIC, "VbSharedDataInit magic"); + TEST_EQ(d->struct_version, VB_SHARED_DATA_VERSION, + "VbSharedDataInit version"); + TEST_EQ(d->struct_size, sizeof(VbSharedDataHeader), + "VbSharedDataInit struct_size"); + TEST_EQ(d->data_size, VB_SHARED_DATA_MIN_SIZE, + "VbSharedDataInit data_size"); + TEST_EQ(d->data_used, d->struct_size, "VbSharedDataInit data_used"); + TEST_EQ(d->firmware_index, 0xFF, "VbSharedDataInit firmware index"); + + /* Sample some other fields to make sure they were zeroed */ + TEST_EQ(d->flags, 0, "VbSharedDataInit firmware flags"); + TEST_EQ(d->lk_call_count, 0, "VbSharedDataInit lk_call_count"); + TEST_EQ(d->kernel_version_lowest, 0, + "VbSharedDataInit kernel_version_lowest"); } - /* disable MSVC warnings on unused arguments */ __pragma(warning (disable: 4100)) -int main(int argc, char* argv[]) { - int error_code = 0; - - StructPackingTest(); - ArraySizeTest(); - VerifyHelperFunctions(); - PublicKeyTest(); - VbSharedDataTest(); - - if (!gTestSuccess) - error_code = 255; +int main(int argc, char* argv[]) +{ + StructPackingTest(); + ArraySizeTest(); + VerifyHelperFunctions(); + PublicKeyTest(); + VbSharedDataTest(); - return error_code; + return gTestSuccess ? 0 : 255; } diff --git a/tests/vboot_display_tests.c b/tests/vboot_display_tests.c new file mode 100644 index 00000000..5713667b --- /dev/null +++ b/tests/vboot_display_tests.c @@ -0,0 +1,83 @@ +/* Copyright (c) 2013 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 display library. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "gbb_header.h" +#include "host_common.h" +#include "test_common.h" +#include "vboot_common.h" +#include "vboot_display.h" +#include "vboot_nvstorage.h" + +/* Mock data */ +static VbCommonParams cparams; +static VbNvContext vnc; +static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE]; +static VbSharedDataHeader *shared = (VbSharedDataHeader *)shared_data; +static GoogleBinaryBlockHeader gbb; +static char debug_info[4096]; + +/* Reset mock data (for use before each test) */ +static void ResetMocks(void) +{ + Memset(&cparams, 0, sizeof(cparams)); + cparams.shared_data_size = sizeof(shared_data); + cparams.shared_data_blob = shared_data; + cparams.gbb_data = &gbb; + + Memset(&gbb, 0, sizeof(gbb)); + gbb.major_version = GBB_MAJOR_VER; + gbb.minor_version = GBB_MINOR_VER; + gbb.flags = 0; + + Memset(&vnc, 0, sizeof(vnc)); + VbNvSetup(&vnc); + VbNvTeardown(&vnc); /* So CRC gets generated */ + + Memset(&shared_data, 0, sizeof(shared_data)); + VbSharedDataInit(shared, sizeof(shared_data)); + + *debug_info = 0; +} + +/* Mocks */ + +VbError_t VbExDisplayDebugInfo(const char *info_str) +{ + strncpy(debug_info, info_str, sizeof(debug_info)); + debug_info[sizeof(debug_info) - 1] = '\0'; + return VBERROR_SUCCESS; +} + +/* Test displaying debug info */ +static void DebugInfoTest(void) +{ + int i; + + /* Recovery string should be non-null for any code */ + for (i = 0; i < 0x100; i++) + TEST_PTR_NEQ(RecoveryReasonString(i), NULL, "Non-null reason"); + + /* Display debug info */ + VbDisplayDebugInfo(&cparams, &vnc); + TEST_NEQ(*debug_info, '\0', "Some debug info was displayed"); +} + +/* disable MSVC warnings on unused arguments */ +__pragma(warning (disable: 4100)) + +int main(int argc, char* argv[]) +{ + ResetMocks(); // KLUDGE + + DebugInfoTest(); + + return gTestSuccess ? 0 : 255; +} diff --git a/tests/vboot_ec_tests.c b/tests/vboot_ec_tests.c deleted file mode 100644 index 831565fc..00000000 --- a/tests/vboot_ec_tests.c +++ /dev/null @@ -1,160 +0,0 @@ -/* Copyright (c) 2011 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 EC firmware vboot stuff. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "cryptolib.h" -#include "file_keys.h" -#include "host_common.h" -#include "test_common.h" -#include "vboot_common.h" - -static void ReSignECPreamble(VbECPreambleHeader* h, - const VbPrivateKey* key) { - VbSignature *sig = CalculateSignature((const uint8_t*)h, - h->preamble_signature.data_size, key); - - SignatureCopy(&h->preamble_signature, sig); - free(sig); -} - - -static void VerifyECPreambleTest(const VbPublicKey* public_key, - const VbPrivateKey* private_key) { - VbECPreambleHeader* hdr; - VbECPreambleHeader* h; - RSAPublicKey* rsa; - unsigned hsize; - - /* Create a dummy signature */ - VbSignature* body_sig = SignatureAlloc(56, 78); - - rsa = PublicKeyToRSA(public_key); - hdr = CreateECPreamble(0x1234, body_sig, private_key, - 0x5678, "Foo bar"); - TEST_NEQ(hdr && rsa, 0, "VerifyECPreamble() prerequisites"); - if (!hdr) - return; - - hsize = (unsigned) hdr->preamble_size; - h = (VbECPreambleHeader*)malloc(hsize + 16384); - - TEST_EQ(VerifyECPreamble(hdr, hsize, rsa), 0, - "VerifyECPreamble() ok using key"); - TEST_NEQ(VerifyECPreamble(hdr, hsize - 1, rsa), 0, - "VerifyECPreamble() size--"); - TEST_EQ(VerifyECPreamble(hdr, hsize + 1, rsa), 0, - "VerifyECPreamble() size++"); - - TEST_EQ(hdr->firmware_version, 0x1234, - "VerifyECPreamble() firmware version"); - TEST_EQ(hdr->flags, 0x5678, - "VerifyECPreamble() flags"); - TEST_EQ(strncmp(hdr->name, "Foo bar", sizeof(hdr->name)), 0, - "VerifyECPreamble() name"); - - /* Care about major version but not minor */ - Memcpy(h, hdr, hsize); - h->header_version_major++; - ReSignECPreamble(h, private_key); - TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0, - "VerifyECPreamble() major++"); - - Memcpy(h, hdr, hsize); - h->header_version_major--; - ReSignECPreamble(h, private_key); - TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0, - "VerifyECPreamble() major--"); - - Memcpy(h, hdr, hsize); - h->header_version_minor++; - ReSignECPreamble(h, private_key); - TEST_EQ(VerifyECPreamble(h, hsize, rsa), 0, - "VerifyECPreamble() minor++"); - - Memcpy(h, hdr, hsize); - h->header_version_minor--; - ReSignECPreamble(h, private_key); - TEST_EQ(VerifyECPreamble(h, hsize, rsa), 0, - "VerifyECPreamble() minor--"); - - /* Check signature */ - Memcpy(h, hdr, hsize); - h->preamble_signature.sig_offset = hsize; - ReSignECPreamble(h, private_key); - TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0, - "VerifyECPreamble() sig off end"); - - Memcpy(h, hdr, hsize); - h->preamble_signature.sig_size--; - ReSignECPreamble(h, private_key); - TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0, - "VerifyECPreamble() sig too small"); - - Memcpy(h, hdr, hsize); - GetSignatureData(&h->body_digest)[0] ^= 0x34; - TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0, - "VerifyECPreamble() sig mismatch"); - - /* Check that we signed header and body sig */ - Memcpy(h, hdr, hsize); - h->preamble_signature.data_size = 4; - h->body_digest.sig_offset = 0; - h->body_digest.sig_size = 0; - ReSignECPreamble(h, private_key); - TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0, - "VerifyECPreamble() didn't sign header"); - - Memcpy(h, hdr, hsize); - h->body_digest.sig_offset = hsize; - ReSignECPreamble(h, private_key); - TEST_NEQ(VerifyECPreamble(h, hsize, rsa), 0, - "VerifyECPreamble() body sig off end"); - - /* TODO: verify with extra padding at end of header. */ - - free(h); - RSAPublicKeyFree(rsa); - free(hdr); -} - - -int main(int argc, char* argv[]) { - VbPrivateKey* signing_private_key = NULL; - VbPublicKey* signing_public_key = NULL; - - int error_code = 0; - - if(argc != 3) { - fprintf(stderr, "Usage: %s <signing privkey> <signing pubkey>", argv[0]); - return -1; - } - - signing_private_key = PrivateKeyRead(argv[1]); - if (!signing_private_key) { - fprintf(stderr, "Error reading signing_private_key\n"); - return 1; - } - - signing_public_key = PublicKeyRead(argv[2]); - if (!signing_public_key) { - fprintf(stderr, "Error reading signing_public_key\n"); - return 1; - } - - VerifyECPreambleTest(signing_public_key, signing_private_key); - - - if (signing_public_key) - free(signing_public_key); - if (signing_private_key) - free(signing_private_key); - - return error_code; -} diff --git a/tests/vboot_nvstorage_test.c b/tests/vboot_nvstorage_test.c index 19937d6d..15c229f3 100644 --- a/tests/vboot_nvstorage_test.c +++ b/tests/vboot_nvstorage_test.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. +/* Copyright (c) 2013 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. * @@ -35,6 +35,7 @@ static VbNvField nvfields[] = { {VBNV_DISABLE_DEV_REQUEST, 0, 1, 0, "disable dev request"}, {VBNV_CLEAR_TPM_OWNER_REQUEST, 0, 1, 0, "clear tpm owner request"}, {VBNV_CLEAR_TPM_OWNER_DONE, 0, 1, 0, "clear tpm owner done"}, + {VBNV_OPROM_NEEDED, 0, 1, 0, "oprom needed"}, {0, 0, 0, 0, NULL} }; @@ -102,6 +103,25 @@ static void VbNvStorageTest(void) { /* That should have changed the CRC */ TEST_NEQ(c.raw[15], goodcrc, "VbNvTeardown() CRC changed due to flags clear"); + /* Test explicitly setting the reset flags again */ + VbNvSetup(&c); + VbNvSet(&c, VBNV_FIRMWARE_SETTINGS_RESET, 1); + VbNvGet(&c, VBNV_FIRMWARE_SETTINGS_RESET, &data); + TEST_EQ(data, 1, "Firmware settings forced reset"); + VbNvSet(&c, VBNV_FIRMWARE_SETTINGS_RESET, 0); + + VbNvSet(&c, VBNV_KERNEL_SETTINGS_RESET, 1); + VbNvGet(&c, VBNV_KERNEL_SETTINGS_RESET, &data); + TEST_EQ(data, 1, "Kernel settings forced reset"); + VbNvSet(&c, VBNV_KERNEL_SETTINGS_RESET, 0); + VbNvTeardown(&c); + + /* Get/set an invalid field */ + VbNvSetup(&c); + TEST_EQ(VbNvGet(&c, -1, &data), 1, "Get invalid setting"); + TEST_EQ(VbNvSet(&c, -1, 0), 1, "Set invalid setting"); + VbNvTeardown(&c); + /* Test other fields */ VbNvSetup(&c); for (vnf = nvfields; vnf->desc; vnf++) { @@ -134,6 +154,19 @@ static void VbNvStorageTest(void) { TEST_EQ(c.regenerate_crc, 0, "No regen CRC if data not changed"); VbNvTeardown(&c); TEST_EQ(c.raw_changed, 0, "No raw change if data not changed"); + + /* Test out-of-range fields mapping to defaults */ + VbNvSetup(&c); + VbNvSet(&c, VBNV_TRY_B_COUNT, 16); + VbNvGet(&c, VBNV_TRY_B_COUNT, &data); + TEST_EQ(data, 15, "Try b count out of range"); + VbNvSet(&c, VBNV_RECOVERY_REQUEST, 0x101); + VbNvGet(&c, VBNV_RECOVERY_REQUEST, &data); + TEST_EQ(data, VBNV_RECOVERY_LEGACY, "Recovery request out of range"); + VbNvSet(&c, VBNV_LOCALIZATION_INDEX, 0x102); + VbNvGet(&c, VBNV_LOCALIZATION_INDEX, &data); + TEST_EQ(data, 0, "Localization index out of range"); + VbNvTeardown(&c); } diff --git a/utility/vbutil_ec.c b/utility/vbutil_ec.c deleted file mode 100644 index 4aa0d3f8..00000000 --- a/utility/vbutil_ec.c +++ /dev/null @@ -1,532 +0,0 @@ -/* Copyright (c) 2012 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. - * - * Verified boot utility for EC firmware - */ - -#include <errno.h> -#include <fcntl.h> -#include <getopt.h> -#include <stddef.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "cryptolib.h" -#include "fmap.h" -#include "host_common.h" -#include "vboot_common.h" - - -/* Command line options */ -enum { - OPT_MODE_SIGN = 1000, - OPT_MODE_VERIFY, - OPT_KEYBLOCK, - OPT_SIGNPUBKEY, - OPT_SIGNPRIVATE, - OPT_VERSION, - OPT_FV, - OPT_KERNELKEY, - OPT_FLAGS, - OPT_NAME, -}; - -static struct option long_opts[] = { - {"sign", 1, 0, OPT_MODE_SIGN }, - {"verify", 1, 0, OPT_MODE_VERIFY }, - {"keyblock", 1, 0, OPT_KEYBLOCK }, - {"signpubkey", 1, 0, OPT_SIGNPUBKEY }, - {"signprivate", 1, 0, OPT_SIGNPRIVATE }, - {"version", 1, 0, OPT_VERSION }, - {"flags", 1, 0, OPT_FLAGS }, - {"name", 1, 0, OPT_NAME }, - {NULL, 0, 0, 0} -}; - - -/* Print help and return error */ -static int PrintHelp(void) { - - puts("vbutil_ec - Verified boot signing utility for EC firmware\n" - "\n" - "This will sign, re-sign, or test a complete EC firmware image.\n" - "The EC image is initially completely unsigned. To make it bootable\n" - "the pubic root key must be installed in the RO section, and each RW\n" - "section must be signed with the appropriate private keys.\n" - "\n" - "To sign an image: vbutil_ec --sign <file> [OPTIONS]\n" - "\n" - "For signing, these options are required:\n" - "\n" - " --keyblock <file> Key block in .keyblock format\n" - " --signprivate <file> Signing private key in .vbprivk format\n" - " --version <number> Firmware version\n" - "\n" - "If the RO public key has not been installed, you will also need\n" - "\n" - " --signpubkey <file> Signing public key in .vbpubk format\n" - "\n" - "Optional args are:\n" - "\n" - " --flags <number> Preamble flags (defaults to 0)\n" - " --name <string> Human-readable description\n" - "\n" - "\n" - "To verify an image: vbutil_ec --verify <file>\n" - "\n"); - return 1; -} - - -static int FindInFmap(FmapHeader *fh, const char *name, - uint8_t *base, uint64_t base_size, - uint8_t **data, uint64_t *size) { - const FmapAreaHeader *ah; - int i; - - ah = (FmapAreaHeader *)(fh + 1); - for (i = 0; i < fh->fmap_nareas; i++) - if (!strncmp(ah[i].area_name, name, FMAP_NAMELEN)) { - if (ah[i].area_size + ah[i].area_offset > base_size) { - printf("FMAP region %s extends off image file\n", name); - return 0; - } - if (data) - *data = base + ah[i].area_offset; - if (size) - *size = ah[i].area_size; - return 1; - } - - return 0; -} - -static int GoodKey(VbPublicKey *key, uint64_t region_size) -{ - uint64_t key_size; - - if (0 != VerifyPublicKeyInside(key, region_size, key)) - return 0; - - if (key->algorithm >= kNumAlgorithms) - return 0; - - /* Currently, TPM only supports 16-bit version */ - if (key->key_version > 0xFFFF) - return 0; - - if (!RSAProcessedKeySize(key->algorithm, &key_size) || - key_size != key->key_size) - return 0; - - return 1; -} - - -/* We build the image file with a non-FF byte at the end of each RW firmware, - * just so we can do this. */ -static uint64_t FindImageEnd(uint8_t *data, uint64_t size) -{ - for (size-- ; size && data[size] == 0xff; size--) - ; - return size; -} - -static void SignImage(const char *filename, - VbKeyBlockHeader *key_block, uint64_t key_block_size, - VbPrivateKey *privkey, uint64_t version, - VbPublicKey *pubkey, uint32_t preamble_flags, - const char *name) { - struct stat sb; - int fd; - void *image; - uint64_t image_size; - FmapHeader* fmap; - VbECPreambleHeader *preamble; - uint8_t *fv_data = 0; - uint8_t *vblock_data = 0; - uint64_t fv_size, vblock_size; - VbSignature* body_digest; - - if (name && strlen(name)+1 > sizeof(preamble->name)) - VbExError("Name string is too long\n"); - - if (0 != stat(filename, &sb)) - VbExError("Can't stat %s: %s\n", filename, strerror(errno)); - - fd = open(filename, O_RDWR); - if (fd < 0) - VbExError("Can't open %s: %s\n", filename, strerror(errno)); - - image = mmap(0, sb.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - if (image == (void *)-1) - VbExError("Can't mmap %s: %s\n", filename, strerror(errno)); - close(fd); /* done with this now */ - - fmap = (FmapHeader *)FmapFind(image, sb.st_size); - if (!fmap) - VbExError("File %s doesn't have an FMAP - can't continue.\n"); - - if (fmap->fmap_size > sb.st_size) - VbExError("FMAP is bigger than file size (%ld vs %ld)\n", - fmap->fmap_size, sb.st_size); - - image_size = sb.st_size; - - /* Install pubkey if provided */ - if (pubkey) { - if (!FindInFmap(fmap, "ROOT_KEY", image, image_size, - &vblock_data, &vblock_size)) - VbExError("Can't find ROOT_KEY in %s\n", filename); - - if (pubkey->key_offset + pubkey->key_size > vblock_size) - VbExError("ROOT_KEY is too small for pubkey (%d bytes, needs %d)\n", - vblock_size, pubkey->key_offset + pubkey->key_size); - - memcpy(vblock_data, pubkey, pubkey->key_offset + pubkey->key_size); - } - - - /* Sign FW A */ - if (!FindInFmap(fmap, "FW_MAIN_A", image, image_size, &fv_data, &fv_size)) - VbExError("Can't find FW_MAIN_A in %s\n", filename); - - if (!FindInFmap(fmap, "VBLOCK_A", image, image_size, - &vblock_data, &vblock_size)) - VbExError("Can't find VBLOCK_A in %s\n", filename); - - fv_size = FindImageEnd(fv_data, fv_size); - - body_digest = CalculateHash(fv_data, fv_size, privkey); - if (!body_digest) - VbExError("Error calculating body digest\n"); - - preamble = CreateECPreamble(version, body_digest, privkey, - preamble_flags, name); - if (!preamble) - VbExError("Error creating preamble.\n"); - - if (key_block_size + preamble->preamble_size > vblock_size) - VbExError("VBLOCK_A is too small for digest (%d bytes, needs %d)\n", - vblock_size, key_block_size + preamble->preamble_size); - - memcpy(vblock_data, key_block, key_block_size); - memcpy(vblock_data + key_block_size, preamble, preamble->preamble_size); - - free(body_digest); - free(preamble); - - - /* Sign FW B - skip if there isn't one */ - if (!FindInFmap(fmap, "FW_MAIN_B", image, image_size, &fv_data, &fv_size) || - !FindInFmap(fmap, "VBLOCK_B", image, image_size, - &vblock_data, &vblock_size)) { - printf("Image does not contain FW B - ignoring that part\n"); - } else { - fv_size = FindImageEnd(fv_data, fv_size); - - body_digest = CalculateHash(fv_data, fv_size, privkey); - if (!body_digest) - VbExError("Error calculating body digest\n"); - - preamble = CreateECPreamble(version, body_digest, privkey, - preamble_flags, name); - if (!preamble) - VbExError("Error creating preamble.\n"); - - if (key_block_size + preamble->preamble_size > vblock_size) - VbExError("VBLOCK_B is too small for digest (%d bytes, needs %d)\n", - vblock_size, key_block_size + preamble->preamble_size); - - memcpy(vblock_data, key_block, key_block_size); - memcpy(vblock_data + key_block_size, preamble, preamble->preamble_size); - - free(body_digest); - free(preamble); - } - - /* Unmap to write changes to disk. */ - if (0 != munmap(image, sb.st_size)) - VbExError("Can't munmap %s: %s\n", filename, strerror(errno)); - - printf("Image signing completed\n"); - -} - -static int Verify(const char *filename) { - struct stat sb; - int fd; - void *image; - uint64_t image_size; - FmapHeader* fmap; - VbECPreambleHeader *preamble; - VbPublicKey *pubkey; - uint64_t pubkey_size; - VbKeyBlockHeader *key_block; - uint64_t key_block_size; - uint8_t *fv_data = 0; - uint64_t fv_size; - VbPublicKey *data_key; - RSAPublicKey* rsa; - int errorcnt = 0; - char buf[80]; - int i; - - if (0 != stat(filename, &sb)) - VbExError("Can't stat %s: %s\n", filename, strerror(errno)); - - fd = open(filename, O_RDONLY); - if (fd < 0) - VbExError("Can't open %s: %s\n", filename, strerror(errno)); - - image = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (image == (void *)-1) - VbExError("Can't mmap %s: %s\n", filename, strerror(errno)); - close(fd); /* done with this now */ - - fmap = (FmapHeader *)FmapFind(image, sb.st_size); - if (!fmap) - VbExError("File %s doesn't have an FMAP - can't continue.\n"); - - if (fmap->fmap_size > sb.st_size) - VbExError("FMAP is bigger than file size (%ld vs %ld)\n", - fmap->fmap_size, sb.st_size); - - image_size = sb.st_size; - - /* Read pubkey */ - if (!FindInFmap(fmap, "ROOT_KEY", image, image_size, - (uint8_t **)&pubkey, &pubkey_size)) { - printf("Can't find ROOT_KEY in %s\n", filename); - errorcnt++; - } else if (!GoodKey(pubkey, pubkey_size)) { - printf("ROOT_KEY is invalid\n"); - errorcnt++; - } else { - printf("ROOT_KEY\n"); - printf(" Algorithm: %" PRIu64 " %s\n", pubkey->algorithm, - (pubkey->algorithm < kNumAlgorithms ? - algo_strings[pubkey->algorithm] : "(invalid)")); - printf(" Key Version: %" PRIu64 "\n", pubkey->key_version); - printf(" Key sha1sum: "); - PrintPubKeySha1Sum(pubkey); - printf("\n"); - } - - for (i = 'A'; i <= 'B'; i++) { - - fv_data = 0; - key_block = 0; - preamble = 0; - - printf("FW %c\n", i); - sprintf(buf, "FW_MAIN_%c", i); - if (!FindInFmap(fmap, buf, image, image_size, &fv_data, &fv_size)) { - printf("Can't find %s in %s\n", buf, filename); - /* Not an error for firmware B */ - if (i != 'B') - errorcnt++; - continue; - } - - sprintf(buf, "VBLOCK_%c", i); - if (!FindInFmap(fmap, buf, image, image_size, - (uint8_t **)&key_block, &key_block_size)) { - printf("Can't find %s in %s\n", buf, filename); - /* Not an error for firmware B */ - if (i != 'B') - errorcnt++; - continue; - } - - if (0 != KeyBlockVerify(key_block, key_block_size, pubkey, !pubkey)) { - printf("Error verifying key block for %s.\n", buf); - errorcnt++; - continue; - } - printf(" Key block:\n"); - data_key = &key_block->data_key; - printf(" Size: %" PRIu64 "\n", - key_block->key_block_size); - printf(" Flags: %" PRIu64 " (ignored)\n", - key_block->key_block_flags); - printf(" Data key algorithm: %" PRIu64 " %s\n", data_key->algorithm, - (data_key->algorithm < kNumAlgorithms ? - algo_strings[data_key->algorithm] : "(invalid)")); - printf(" Data key version: %" PRIu64 "\n", data_key->key_version); - printf(" Data key sha1sum: "); - PrintPubKeySha1Sum(data_key); - printf("\n"); - - preamble = (VbECPreambleHeader*) - ((uint8_t *)key_block + key_block->key_block_size); - - rsa = PublicKeyToRSA(&key_block->data_key); - if (!rsa) { - printf("Error parsing data key.\n"); - errorcnt++; - } - /* Verify preamble */ - if (0 != VerifyECPreamble(preamble, - key_block_size - key_block->key_block_size, - rsa)) { - printf("Error verifying preamble.\n"); - errorcnt++; - free(rsa); - continue; - } - printf(" Preamble:\n"); - printf(" Size: %" PRIu64 "\n", - preamble->preamble_size); - printf(" Header version: %" PRIu32 ".%" PRIu32"\n", - preamble->header_version_major, - preamble->header_version_minor); - printf(" Firmware version: %" PRIu64 "\n", - preamble->firmware_version); - printf(" Firmware body size: %" PRIu64 "\n", - preamble->body_digest.data_size); - printf(" Preamble flags: %" PRIu32 "\n", preamble->flags); - printf(" Preamble name: %s\n", preamble->name); - - /* TODO: verify body size same as signature size */ - - /* Verify body */ - if (preamble->flags & VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL) { - printf("Preamble requests USE_RO_NORMAL; skipping verification.\n"); - } else { - if (0 != EqualData(fv_data, fv_size, - &preamble->body_digest, rsa)) { - printf("Error verifying firmware body.\n"); - errorcnt++; - } - } - free(rsa); - } - - /* Done */ - if (0 != munmap(image, sb.st_size)) - VbExError("Can't munmap %s: %s\n", filename, strerror(errno)); - - printf("Done\n"); - return errorcnt; -} - -int main(int argc, char* argv[]) { - - char* filename = NULL; - uint64_t version = 0; - int got_version = 0; - uint32_t preamble_flags = 0; - char *name = NULL; - int mode = 0; - VbKeyBlockHeader* key_block = 0; - VbPrivateKey* privkey = 0; - VbPublicKey* pubkey = 0; - uint64_t key_block_size; - int errorcnt = 0; - char* e; - int i; - - while ((i = getopt_long(argc, argv, "", long_opts, NULL)) != -1) { - switch (i) { - case '?': - /* Unhandled option */ - printf("Unknown option\n"); - errorcnt++; - break; - - case OPT_MODE_SIGN: - case OPT_MODE_VERIFY: - mode = i; - filename = optarg; - break; - - case OPT_KEYBLOCK: - /* Read the key block and keys */ - key_block = (VbKeyBlockHeader*)ReadFile(optarg, &key_block_size); - if (!key_block) { - printf("Error reading key block from %s\n", optarg); - errorcnt++; - } - break; - - case OPT_SIGNPUBKEY: - pubkey = PublicKeyRead(optarg); - if (!pubkey) { - printf("Error reading public key from %s\n", optarg); - errorcnt++; - } - break; - - case OPT_SIGNPRIVATE: - privkey = PrivateKeyRead(optarg); - if (!privkey) { - printf("Error reading private key from %s\n", optarg); - errorcnt++; - } - break; - - case OPT_VERSION: - version = strtoul(optarg, &e, 0); - if (!*optarg || (e && *e)) { - printf("Invalid --version argument: \"%s\"\n", optarg); - errorcnt++; - } - got_version = 1; - break; - - case OPT_FLAGS: - preamble_flags = strtoul(optarg, &e, 0); - if (!*optarg || (e && *e)) { - printf("Invalid --flags argument: \"%s\"\n", optarg); - errorcnt++; - } - break; - - case OPT_NAME: - name = optarg; - break; - } - } - - switch(mode) { - - case OPT_MODE_SIGN: - /* Check required args */ - if (!key_block) { - printf("The ----keyblock arg is required when signing\n"); - errorcnt++; - } - if (!privkey) { - printf("The --signprivate arg is required when signing\n"); - errorcnt++; - } - if (!got_version) { - printf("The --version arg is required when signing\n"); - errorcnt++; - } - - if (errorcnt) - return PrintHelp(); - - /* Sign or die */ - SignImage(filename, key_block, key_block_size, - privkey, version, pubkey, preamble_flags, name); - - /* fall through and verify what we've just done */ - - case OPT_MODE_VERIFY: - return Verify(filename); - - default: - printf("\nMust specify a mode, either --sign or --verify.\n\n"); - return PrintHelp(); - } -} |