diff options
author | Bill Richardson <wfrichar@chromium.org> | 2013-02-13 09:05:00 -0800 |
---|---|---|
committer | Bill Richardson <wfrichar@chromium.org> | 2013-02-13 09:05:55 -0800 |
commit | 7f5e3d744af83584c307b9dc2b6ec36f8b308285 (patch) | |
tree | 010dc3fa99ea54ad731910327950a463b6fa52e7 | |
parent | 4c3f6e0e16844db159ccaa7e1f8fe8fa5a1d6e01 (diff) | |
parent | 886a9047f07d6bf9f424fd83247136c79706e136 (diff) | |
download | vboot-7f5e3d744af83584c307b9dc2b6ec36f8b308285.tar.gz |
Support installing futility on target
There is a corresponding ebuild change needed to make this work. I'll merge
that into ToT Real Soon Now, then this will work.
BUG=chromium-os:37062
BRANCH=none
TEST=none
Everything should continue to work as before.
Change-Id: Iad103183938f0dd42a18fd14fc96b35fb58b4413
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
46 files changed, 7296 insertions, 4325 deletions
@@ -35,14 +35,15 @@ # Our convention is that we only use := for variables that will never be # changed or appended. They must be defined before being used anywhere. -# we should only run pwd once, not every time we refer to ${BUILD}. +# We should only run pwd once, not every time we refer to ${BUILD}. SRCDIR := $(shell pwd) BUILD ?= $(SRCDIR)/build export BUILD -# Target for 'make install' -DESTDIR ?= /usr/bin +# Stuff for 'make install' INSTALL ?= install +DESTDIR ?= /usr/bin +O_DESTDIR = ${DESTDIR}/old_bins # Where to install the (exportable) executables for testing? TEST_INSTALL_DIR = ${BUILD}/install_for_test @@ -322,16 +323,20 @@ CGPT_SRCS = \ CGPT_OBJS = ${CGPT_SRCS:%.c=${BUILD}/%.o} ALL_OBJS += ${CGPT_OBJS} -C_DESTDIR = ${DESTDIR}/old_bins +C_DESTDIR = ${O_DESTDIR} # Scripts to install directly (not compiled) UTIL_SCRIPTS = \ utility/dev_debug_vboot \ - utility/dev_make_keypair \ utility/enable_dev_usb_boot \ utility/vbutil_what_keys +ifeq (${MINIMAL},) +UTIL_SCRIPTS += \ + utility/dev_make_keypair +endif + # These utilities should be linked statically. UTIL_NAMES_STATIC = \ crossystem \ @@ -342,39 +347,61 @@ UTIL_NAMES = ${UTIL_NAMES_STATIC} \ dev_sign_file \ dump_kernel_config \ dumpRSAPublicKey \ - load_kernel_test \ - pad_digest_utility \ - signature_digest_utility \ tpm_init_temp_fix \ tpmc \ vbutil_firmware \ vbutil_kernel \ vbutil_key \ vbutil_keyblock \ - verify_data - -ifneq (${IN_CHROOT},) -UTIL_NAMES += mount-encrypted -endif ifeq (${MINIMAL},) UTIL_NAMES += \ bmpblk_font \ bmpblk_utility \ eficompress \ - efidecompress + efidecompress \ + load_kernel_test \ + pad_digest_utility \ + signature_digest_utility \ + verify_data +endif + +ifneq (${IN_CHROOT},) +UTIL_NAMES += mount-encrypted endif UTIL_BINS_STATIC := $(addprefix ${BUILD}/utility/,${UTIL_NAMES_STATIC}) UTIL_BINS = $(addprefix ${BUILD}/utility/,${UTIL_NAMES}) ALL_DEPS += $(addsuffix .d,${UTIL_BINS}) -U_DESTDIR = ${DESTDIR}/old_bins +U_DESTDIR = ${O_DESTDIR} + +# Scripts for signing stuff. +SIGNING_SCRIPTS = \ + utility/tpm-nvsize \ + utility/chromeos-tpm-recovery +# We need these wrapped, but the symlinks go in different places. +SIGNING_SCRIPTS_DEV = \ + scripts/image_signing/resign_firmwarefd.sh \ + scripts/image_signing/make_dev_firmware.sh \ + scripts/image_signing/make_dev_ssd.sh \ + scripts/image_signing/set_gbb_flags.sh + +# Installed, but not made executable. +SIGNING_COMMON = scripts/image_signing/common_minimal.sh # The unified firmware utility will eventually replace all the others FUTIL_BIN = ${BUILD}/futility/futility +# These are the others it will replace. +FUTIL_OLD = $(notdir ${CGPT} ${UTIL_BINS} ${UTIL_SCRIPTS} ${SIGNING_SCRIPTS}) + +# The ebuild will put these somewhere else on the target. +ifeq (${MINIMAL},) +FUTIL_OLD += $(notdir ${SIGNING_SCRIPTS_DEV}) +endif + FUTIL_SRCS = \ futility/futility.c \ futility/cmd_foo.c @@ -412,6 +439,7 @@ TEST_NAMES = \ sha_benchmark \ sha_tests \ stateful_util_tests \ + tlcl_tests \ tpm_bootmode_tests \ utility_string_tests \ utility_tests \ @@ -419,12 +447,16 @@ TEST_NAMES = \ vboot_api_devmode_tests \ vboot_api_firmware_tests \ vboot_api_kernel_tests \ + vboot_api_kernel2_tests \ + vboot_api_kernel3_tests \ + vboot_api_kernel4_tests \ vboot_audio_tests \ vboot_common_tests \ vboot_common2_tests \ vboot_common3_tests \ vboot_display_tests \ vboot_firmware_tests \ + vboot_kernel_tests \ vboot_nvstorage_test # Grrr @@ -646,6 +678,16 @@ utils_install: ${UTIL_BINS} ${UTIL_SCRIPTS} ${Q}mkdir -p ${U_DESTDIR} ${Q}${INSTALL} -t ${U_DESTDIR} $^ +# And some signing stuff... + +.PHONY: signing_install +signing_install: ${SIGNING_SCRIPTS} ${SIGNING_SCRIPTS_DEV} ${SIGNING_COMMON} + @printf " INSTALL SIGNING\n" + ${Q}mkdir -p ${U_DESTDIR} + ${Q}${INSTALL} -t ${U_DESTDIR} ${SIGNING_SCRIPTS} + ${Q}${INSTALL} -t ${U_DESTDIR} ${SIGNING_SCRIPTS_DEV} + ${Q}${INSTALL} -t ${U_DESTDIR} -m 'u=rw,go=r,a-s' ${SIGNING_COMMON} + # ---------------------------------------------------------------------------- # new Firmware Utility @@ -657,12 +699,13 @@ ${FUTIL_BIN}: ${FUTIL_LDS} ${FUTIL_OBJS} ${Q}${LD} -o $@ ${CFLAGS} $^ ${LDFLAGS} ${LDLIBS} .PHONY: futil_install -futil_install: ${FUTIL_BIN} cgpt_install utils_install +futil_install: ${FUTIL_BIN} cgpt_install utils_install signing_install @printf " INSTALL futility\n" ${Q}mkdir -p ${F_DESTDIR} ${Q}${INSTALL} -t ${F_DESTDIR} ${FUTIL_BIN} - futility/setup_futility_symlinks.sh ${F_DESTDIR} - + ${Q}mkdir -p ${O_DESTDIR} + ${Q}for prog in ${FUTIL_OLD}; do \ + ln -sf futility "${F_DESTDIR}/$$prog"; done # ---------------------------------------------------------------------------- # Mount-encrypted utility for cryptohome @@ -844,6 +887,11 @@ ${BUILD}/tests/rollback_index2_tests: OBJS += \ ${BUILD}/tests/rollback_index2_tests: \ ${BUILD}/firmware/lib/rollback_index_for_test.o +${BUILD}/tests/tlcl_tests: OBJS += \ + ${BUILD}/firmware/lib/tpm_lite/tlcl_for_test.o +${BUILD}/tests/tlcl_tests: \ + ${BUILD}/firmware/lib/tpm_lite/tlcl_for_test.o + ${BUILD}/tests/vboot_audio_tests: OBJS += \ ${BUILD}/firmware/lib/vboot_audio_for_test.o ${BUILD}/tests/vboot_audio_tests: \ @@ -939,18 +987,24 @@ runmisctests: test_setup ${RUNTEST} ${BUILD_RUN}/tests/rsa_utility_tests ${RUNTEST} ${BUILD_RUN}/tests/sha_tests ${RUNTEST} ${BUILD_RUN}/tests/stateful_util_tests + ${RUNTEST} ${BUILD_RUN}/tests/tlcl_tests ${RUNTEST} ${BUILD_RUN}/tests/tpm_bootmode_tests ${RUNTEST} ${BUILD_RUN}/tests/utility_string_tests ${RUNTEST} ${BUILD_RUN}/tests/utility_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_devmode_tests - ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_init_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_firmware_tests + ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_init_tests + ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel_tests + ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel2_tests + ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel3_tests + ${RUNTEST} ${BUILD_RUN}/tests/vboot_api_kernel4_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_kernel_tests ${RUNTEST} ${BUILD_RUN}/tests/vboot_nvstorage_test .PHONY: runfutiltests @@ -992,7 +1046,8 @@ coverage_html: # 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/*' \ + lcov -r ${COV_INFO}.local '*/stub/*' -o ${COV_INFO}.nostub + lcov -e ${COV_INFO}.nostub '${SRCDIR}/firmware/*' \ -o ${COV_INFO}.firmware .PHONY: coverage diff --git a/PRESUBMIT.cfg b/PRESUBMIT.cfg new file mode 100644 index 00000000..2e8cdb66 --- /dev/null +++ b/PRESUBMIT.cfg @@ -0,0 +1,6 @@ +[Hook Overrides] + +# We are using Linux style indentation with tabs +# The indentation is checked by checkpatch not the python script +tab_check: false + diff --git a/firmware/include/tlcl.h b/firmware/include/tlcl.h index ca4e47f7..5ce05638 100644 --- a/firmware/include/tlcl.h +++ b/firmware/include/tlcl.h @@ -134,6 +134,8 @@ uint32_t TlclPhysicalPresenceCMDEnable(void); */ uint32_t TlclFinalizePhysicalPresence(void); +uint32_t TlclAssertPhysicalPresenceResult(void); + /** * Turn off physical presence and locks it off until next reboot. The TPM * error code is returned. diff --git a/firmware/include/vboot_api.h b/firmware/include/vboot_api.h index d91db7d8..31e91a7b 100644 --- a/firmware/include/vboot_api.h +++ b/firmware/include/vboot_api.h @@ -105,7 +105,11 @@ enum VbErrorPredefined_t { /* Need VGA and don't have it, or vice-versa */ VBERROR_VGA_OPROM_MISMATCH = 0x10021, /* Need EC to reboot to read-only code */ - VBERROR_EC_REBOOT_TO_RO_REQUIRED = 0x10022 + VBERROR_EC_REBOOT_TO_RO_REQUIRED = 0x10022, + + /* VbExEcGetExpectedRWHash() may return the following codes */ + /* Compute expected RW hash from the EC image; BIOS doesn't have it */ + VBERROR_EC_GET_EXPECTED_HASH_FROM_IMAGE = 0x20000, }; @@ -755,6 +759,13 @@ VbError_t VbExEcGetExpectedRW(enum VbSelectFirmware_t select, const uint8_t **image, int *image_size); /** + * Read the SHA-256 hash of the expected contents of the EC image associated + * with the main firmware specified by the "select" argument. + */ +VbError_t VbExEcGetExpectedRWHash(enum VbSelectFirmware_t select, + const uint8_t **hash, int *hash_size); + +/** * Update the EC rewritable image. */ VbError_t VbExEcUpdateRW(const uint8_t *image, int image_size); diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h index 890af4b6..943a5664 100644 --- a/firmware/include/vboot_nvstorage.h +++ b/firmware/include/vboot_nvstorage.h @@ -142,6 +142,10 @@ typedef enum VbNvParam { #define VBNV_RECOVERY_EC_JUMP_RW 0x27 /* EC software sync - unable to protect / unprotect EC-RW */ #define VBNV_RECOVERY_EC_PROTECT 0x28 +/* EC software sync - error obtaining expected EC hash */ +#define VBNV_RECOVERY_EC_EXPECTED_HASH 0x29 +/* EC software sync - expected EC image doesn't match hash */ +#define VBNV_RECOVERY_EC_HASH_MISMATCH 0x2A /* Unspecified/unknown error in read-only firmware */ #define VBNV_RECOVERY_RO_UNSPECIFIED 0x3F /* diff --git a/firmware/lib/cgptlib/cgptlib.c b/firmware/lib/cgptlib/cgptlib.c index c3f37442..370530f1 100644 --- a/firmware/lib/cgptlib/cgptlib.c +++ b/firmware/lib/cgptlib/cgptlib.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. */ @@ -10,150 +10,173 @@ #include "utility.h" #include "vboot_api.h" -int GptInit(GptData *gpt) { - int retval; +int GptInit(GptData *gpt) +{ + int retval; - gpt->modified = 0; - gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; - gpt->current_priority = 999; + gpt->modified = 0; + gpt->current_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; + gpt->current_priority = 999; - retval = GptSanityCheck(gpt); - if (GPT_SUCCESS != retval) { - VBDEBUG(("GptInit() failed sanity check\n")); - return retval; - } + retval = GptSanityCheck(gpt); + if (GPT_SUCCESS != retval) { + VBDEBUG(("GptInit() failed sanity check\n")); + return retval; + } - GptRepair(gpt); - return GPT_SUCCESS; + GptRepair(gpt); + return GPT_SUCCESS; } - -int GptNextKernelEntry(GptData* gpt, uint64_t* start_sector, uint64_t* size) { - GptHeader* header = (GptHeader*)gpt->primary_header; - GptEntry* entries = (GptEntry*)gpt->primary_entries; - GptEntry* e; - int new_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; - int new_prio = 0; - uint32_t i; - - /* If we already found a kernel, continue the scan at the current - * kernel's prioity, in case there is another kernel with the same - * priority. */ - if (gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND) { - for (i = gpt->current_kernel + 1; i < header->number_of_entries; i++) { - e = entries + i; - if (!IsKernelEntry(e)) - continue; - VBDEBUG(("GptNextKernelEntry looking at same prio partition %d\n", i+1)); - VBDEBUG(("GptNextKernelEntry s%d t%d p%d\n", - GetEntrySuccessful(e), GetEntryTries(e), GetEntryPriority(e))); - if (!(GetEntrySuccessful(e) || GetEntryTries(e))) - continue; - if (GetEntryPriority(e) == gpt->current_priority) { - gpt->current_kernel = i; - *start_sector = e->starting_lba; - *size = e->ending_lba - e->starting_lba + 1; - VBDEBUG(("GptNextKernelEntry likes that one\n")); - return GPT_SUCCESS; - } - } - } - - /* We're still here, so scan for the remaining kernel with the - * highest priority less than the previous attempt. */ - for (i = 0, e = entries; i < header->number_of_entries; i++, e++) { - int current_prio = GetEntryPriority(e); - if (!IsKernelEntry(e)) - continue; - VBDEBUG(("GptNextKernelEntry looking at new prio partition %d\n", i+1)); - VBDEBUG(("GptNextKernelEntry s%d t%d p%d\n", - GetEntrySuccessful(e), GetEntryTries(e), GetEntryPriority(e))); - if (!(GetEntrySuccessful(e) || GetEntryTries(e))) - continue; - if (current_prio >= gpt->current_priority) - continue; /* Already returned this kernel in a previous call */ - if (current_prio > new_prio) { - new_kernel = i; - new_prio = current_prio; - } - } - - /* Save what we found. Note that if we didn't find a new kernel, - * new_prio will still be -1, so future calls to this function will - * also fail. */ - gpt->current_kernel = new_kernel; - gpt->current_priority = new_prio; - - if (CGPT_KERNEL_ENTRY_NOT_FOUND == new_kernel) { - VBDEBUG(("GptNextKernelEntry no more kernels\n")); - return GPT_ERROR_NO_VALID_KERNEL; - } - - VBDEBUG(("GptNextKernelEntry likes partition %d\n", new_kernel+1)); - e = entries + new_kernel; - *start_sector = e->starting_lba; - *size = e->ending_lba - e->starting_lba + 1; - return GPT_SUCCESS; +int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size) +{ + GptHeader *header = (GptHeader *)gpt->primary_header; + GptEntry *entries = (GptEntry *)gpt->primary_entries; + GptEntry *e; + int new_kernel = CGPT_KERNEL_ENTRY_NOT_FOUND; + int new_prio = 0; + uint32_t i; + + /* + * If we already found a kernel, continue the scan at the current + * kernel's priority, in case there is another kernel with the same + * priority. + */ + if (gpt->current_kernel != CGPT_KERNEL_ENTRY_NOT_FOUND) { + for (i = gpt->current_kernel + 1; + i < header->number_of_entries; i++) { + e = entries + i; + if (!IsKernelEntry(e)) + continue; + VBDEBUG(("GptNextKernelEntry looking at same prio " + "partition %d\n", i+1)); + VBDEBUG(("GptNextKernelEntry s%d t%d p%d\n", + GetEntrySuccessful(e), GetEntryTries(e), + GetEntryPriority(e))); + if (!(GetEntrySuccessful(e) || GetEntryTries(e))) + continue; + if (GetEntryPriority(e) == gpt->current_priority) { + gpt->current_kernel = i; + *start_sector = e->starting_lba; + *size = e->ending_lba - e->starting_lba + 1; + VBDEBUG(("GptNextKernelEntry likes it\n")); + return GPT_SUCCESS; + } + } + } + + /* + * We're still here, so scan for the remaining kernel with the highest + * priority less than the previous attempt. + */ + for (i = 0, e = entries; i < header->number_of_entries; i++, e++) { + int current_prio = GetEntryPriority(e); + if (!IsKernelEntry(e)) + continue; + VBDEBUG(("GptNextKernelEntry looking at new prio " + "partition %d\n", i+1)); + VBDEBUG(("GptNextKernelEntry s%d t%d p%d\n", + GetEntrySuccessful(e), GetEntryTries(e), + GetEntryPriority(e))); + if (!(GetEntrySuccessful(e) || GetEntryTries(e))) + continue; + if (current_prio >= gpt->current_priority) { + /* Already returned this kernel in a previous call */ + continue; + } + if (current_prio > new_prio) { + new_kernel = i; + new_prio = current_prio; + } + } + + /* + * Save what we found. Note that if we didn't find a new kernel, + * new_prio will still be -1, so future calls to this function will + * also fail. + */ + gpt->current_kernel = new_kernel; + gpt->current_priority = new_prio; + + if (CGPT_KERNEL_ENTRY_NOT_FOUND == new_kernel) { + VBDEBUG(("GptNextKernelEntry no more kernels\n")); + return GPT_ERROR_NO_VALID_KERNEL; + } + + VBDEBUG(("GptNextKernelEntry likes partition %d\n", new_kernel + 1)); + e = entries + new_kernel; + *start_sector = e->starting_lba; + *size = e->ending_lba - e->starting_lba + 1; + return GPT_SUCCESS; } - -int GptUpdateKernelEntry(GptData* gpt, uint32_t update_type) { - GptHeader* header = (GptHeader*)gpt->primary_header; - GptEntry* entries = (GptEntry*)gpt->primary_entries; - GptEntry* e = entries + gpt->current_kernel; - uint16_t previous_attr = e->attrs.fields.gpt_att; - - if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) - return GPT_ERROR_INVALID_UPDATE_TYPE; - if (!IsKernelEntry(e)) - return GPT_ERROR_INVALID_UPDATE_TYPE; - - switch (update_type) { - case GPT_UPDATE_ENTRY_TRY: { - /* Used up a try */ - int tries; - if (GetEntrySuccessful(e)) - return GPT_SUCCESS; /* Successfully booted this partition, so - * tries field is ignored. */ - tries = GetEntryTries(e); - if (tries > 1) { - /* Still have tries left */ - SetEntryTries(e, tries - 1); - break; - } - /* Out of tries, so drop through and mark partition bad. */ - } - case GPT_UPDATE_ENTRY_BAD: { - /* Giving up on this partition entirely. */ - if (!GetEntrySuccessful(e)) { - /* Only clear tries and priority if the successful bit is not set. */ - e->attrs.fields.gpt_att = previous_attr & ~( - CGPT_ATTRIBUTE_TRIES_MASK | - CGPT_ATTRIBUTE_PRIORITY_MASK); - } - break; - } - default: - return GPT_ERROR_INVALID_UPDATE_TYPE; - } - - /* If no change to attributes, we're done */ - if (e->attrs.fields.gpt_att == previous_attr) - return GPT_SUCCESS; - - /* Update the CRCs */ - header->entries_crc32 = Crc32((const uint8_t *)entries, - header->size_of_entry * - header->number_of_entries); - header->header_crc32 = HeaderCrc(header); - gpt->modified |= GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1; - - /* Use the repair function to update the other copy of the GPT. - * This is a tad inefficient, but is much faster than the disk I/O - * to update the GPT on disk so it doesn't matter. */ - gpt->valid_headers = MASK_PRIMARY; - gpt->valid_entries = MASK_PRIMARY; - GptRepair(gpt); - - return GPT_SUCCESS; +int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type) +{ + GptHeader *header = (GptHeader *)gpt->primary_header; + GptEntry *entries = (GptEntry *)gpt->primary_entries; + GptEntry *e = entries + gpt->current_kernel; + uint16_t previous_attr = e->attrs.fields.gpt_att; + + if (gpt->current_kernel == CGPT_KERNEL_ENTRY_NOT_FOUND) + return GPT_ERROR_INVALID_UPDATE_TYPE; + if (!IsKernelEntry(e)) + return GPT_ERROR_INVALID_UPDATE_TYPE; + + switch (update_type) { + case GPT_UPDATE_ENTRY_TRY: { + /* Used up a try */ + int tries; + if (GetEntrySuccessful(e)) { + /* + * Successfully booted this partition, so tries field + * is ignored. + */ + return GPT_SUCCESS; + } + tries = GetEntryTries(e); + if (tries > 1) { + /* Still have tries left */ + SetEntryTries(e, tries - 1); + break; + } + /* Out of tries, so drop through and mark partition bad. */ + } + case GPT_UPDATE_ENTRY_BAD: { + /* Giving up on this partition entirely. */ + if (!GetEntrySuccessful(e)) { + /* + * Only clear tries and priority if the successful bit + * is not set. + */ + e->attrs.fields.gpt_att = previous_attr & + ~(CGPT_ATTRIBUTE_TRIES_MASK | + CGPT_ATTRIBUTE_PRIORITY_MASK); + } + break; + } + default: + return GPT_ERROR_INVALID_UPDATE_TYPE; + } + + /* If no change to attributes, we're done */ + if (e->attrs.fields.gpt_att == previous_attr) + return GPT_SUCCESS; + + /* Update the CRCs */ + header->entries_crc32 = Crc32((const uint8_t *)entries, + header->size_of_entry * + header->number_of_entries); + header->header_crc32 = HeaderCrc(header); + gpt->modified |= GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1; + + /* + * Use the repair function to update the other copy of the GPT. This + * is a tad inefficient, but is much faster than the disk I/O to update + * the GPT on disk so it doesn't matter. + */ + gpt->valid_headers = MASK_PRIMARY; + gpt->valid_entries = MASK_PRIMARY; + GptRepair(gpt); + + return GPT_SUCCESS; } diff --git a/firmware/lib/cgptlib/cgptlib_internal.c b/firmware/lib/cgptlib/cgptlib_internal.c index 86b2c151..d51ce33b 100644 --- a/firmware/lib/cgptlib/cgptlib_internal.c +++ b/firmware/lib/cgptlib/cgptlib_internal.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. */ @@ -10,396 +10,410 @@ #include "utility.h" -int CheckParameters(GptData *gpt) { - /* Currently, we only support 512-byte sector. In the future, we may support - * larger sector. */ - if (gpt->sector_bytes != 512) - return GPT_ERROR_INVALID_SECTOR_SIZE; - - /* The sector number of a drive should be reasonable. If the given value is - * too small to contain basic GPT structure (PMBR + Headers + Entries), - * the value is wrong. */ - if (gpt->drive_sectors < (1 + 2 * (1 + GPT_ENTRIES_SECTORS))) - return GPT_ERROR_INVALID_SECTOR_NUMBER; - - return GPT_SUCCESS; +int CheckParameters(GptData *gpt) +{ + /* Currently, we only support 512-byte sectors. */ + if (gpt->sector_bytes != 512) + return GPT_ERROR_INVALID_SECTOR_SIZE; + + /* + * Sector count of a drive should be reasonable. If the given value is + * too small to contain basic GPT structure (PMBR + Headers + Entries), + * the value is wrong. + */ + if (gpt->drive_sectors < (1 + 2 * (1 + GPT_ENTRIES_SECTORS))) + return GPT_ERROR_INVALID_SECTOR_NUMBER; + + return GPT_SUCCESS; } +uint32_t HeaderCrc(GptHeader *h) +{ + uint32_t crc32, original_crc32; -uint32_t HeaderCrc(GptHeader* h) { - uint32_t crc32, original_crc32; - - /* Original CRC is calculated with the CRC field 0. */ - original_crc32 = h->header_crc32; - h->header_crc32 = 0; - crc32 = Crc32((const uint8_t *)h, h->size); - h->header_crc32 = original_crc32; + /* Original CRC is calculated with the CRC field 0. */ + original_crc32 = h->header_crc32; + h->header_crc32 = 0; + crc32 = Crc32((const uint8_t *)h, h->size); + h->header_crc32 = original_crc32; - return crc32; + return crc32; } - -int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors) { - if (!h) - return 1; - - /* Make sure we're looking at a header of reasonable size before - * attempting to calculate CRC. */ - if (Memcmp(h->signature, GPT_HEADER_SIGNATURE, GPT_HEADER_SIGNATURE_SIZE) && - Memcmp(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE)) - return 1; - if (h->revision != GPT_HEADER_REVISION) - return 1; - if (h->size < MIN_SIZE_OF_HEADER || h->size > MAX_SIZE_OF_HEADER) - return 1; - - /* Check CRC before looking at remaining fields */ - if (HeaderCrc(h) != h->header_crc32) - return 1; - - /* Reserved fields must be zero. */ - if (h->reserved_zero) - return 1; - - /* Could check that padding is zero, but that doesn't matter to us. */ - - /* If entry size is different than our struct, we won't be able to - * parse it. Technically, any size 2^N where N>=7 is valid. */ - if (h->size_of_entry != sizeof(GptEntry)) - return 1; - if ((h->number_of_entries < MIN_NUMBER_OF_ENTRIES) || - (h->number_of_entries > MAX_NUMBER_OF_ENTRIES) || - (h->number_of_entries * h->size_of_entry != TOTAL_ENTRIES_SIZE)) - return 1; - - /* Check locations for the header and its entries. The primary - * immediately follows the PMBR, and is followed by its entries. - * The secondary is at the end of the drive, preceded by its - * entries. */ - if (is_secondary) { - if (h->my_lba != drive_sectors - 1) - return 1; - if (h->entries_lba != h->my_lba - GPT_ENTRIES_SECTORS) - return 1; - } else { - if (h->my_lba != 1) - return 1; - if (h->entries_lba != h->my_lba + 1) - return 1; - } - - /* 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. */ - if (h->first_usable_lba < 2 + GPT_ENTRIES_SECTORS) - return 1; - if (h->last_usable_lba >= drive_sectors - 1 - GPT_ENTRIES_SECTORS) - return 1; - if (h->first_usable_lba > h->last_usable_lba) - return 1; - - /* Success */ - return 0; +int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors) +{ + if (!h) + return 1; + + /* + * Make sure we're looking at a header of reasonable size before + * attempting to calculate CRC. + */ + if (Memcmp(h->signature, GPT_HEADER_SIGNATURE, + GPT_HEADER_SIGNATURE_SIZE) && + Memcmp(h->signature, GPT_HEADER_SIGNATURE2, + GPT_HEADER_SIGNATURE_SIZE)) + return 1; + if (h->revision != GPT_HEADER_REVISION) + return 1; + if (h->size < MIN_SIZE_OF_HEADER || h->size > MAX_SIZE_OF_HEADER) + return 1; + + /* Check CRC before looking at remaining fields */ + if (HeaderCrc(h) != h->header_crc32) + return 1; + + /* Reserved fields must be zero. */ + if (h->reserved_zero) + return 1; + + /* Could check that padding is zero, but that doesn't matter to us. */ + + /* + * If entry size is different than our struct, we won't be able to + * parse it. Technically, any size 2^N where N>=7 is valid. + */ + if (h->size_of_entry != sizeof(GptEntry)) + return 1; + if ((h->number_of_entries < MIN_NUMBER_OF_ENTRIES) || + (h->number_of_entries > MAX_NUMBER_OF_ENTRIES) || + (h->number_of_entries * h->size_of_entry != TOTAL_ENTRIES_SIZE)) + return 1; + + /* + * Check locations for the header and its entries. The primary + * immediately follows the PMBR, and is followed by its entries. The + * secondary is at the end of the drive, preceded by its entries. + */ + if (is_secondary) { + if (h->my_lba != drive_sectors - 1) + return 1; + if (h->entries_lba != h->my_lba - GPT_ENTRIES_SECTORS) + return 1; + } else { + if (h->my_lba != 1) + return 1; + if (h->entries_lba != h->my_lba + 1) + return 1; + } + + /* + * 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. + */ + if (h->first_usable_lba < 2 + GPT_ENTRIES_SECTORS) + return 1; + if (h->last_usable_lba >= drive_sectors - 1 - GPT_ENTRIES_SECTORS) + return 1; + if (h->first_usable_lba > h->last_usable_lba) + return 1; + + /* Success */ + return 0; } - -/* Return non-zero if the entry is unused, 0 if it is used. */ -int IsUnusedEntry(const GptEntry* e) { - static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}}; - return !Memcmp(&zero, (const uint8_t*)(&e->type), sizeof(zero)); +int IsUnusedEntry(const GptEntry *e) +{ + static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}}; + return !Memcmp(&zero, (const uint8_t*)(&e->type), sizeof(zero)); } -/* Returns non-zero if the entry is a Chrome OS kernel partition, else 0. */ -int IsKernelEntry(const GptEntry* e) { - static Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; - return !Memcmp(&e->type, &chromeos_kernel, sizeof(Guid)); +int IsKernelEntry(const GptEntry *e) +{ + static Guid chromeos_kernel = GPT_ENT_TYPE_CHROMEOS_KERNEL; + return !Memcmp(&e->type, &chromeos_kernel, sizeof(Guid)); } - -int CheckEntries(GptEntry* entries, GptHeader* h) { - - GptEntry* entry; - uint32_t crc32; - uint32_t i; - - /* Check CRC before examining entries. */ - crc32 = Crc32((const uint8_t *)entries, - h->size_of_entry * h->number_of_entries); - if (crc32 != h->entries_crc32) - return GPT_ERROR_CRC_CORRUPTED; - - /* Check all entries. */ - for (i = 0, entry = entries; i < h->number_of_entries; i++, entry++) { - GptEntry* e2; - uint32_t i2; - - if (IsUnusedEntry(entry)) - continue; - - /* Entry must be in valid region. */ - if ((entry->starting_lba < h->first_usable_lba) || - (entry->ending_lba > h->last_usable_lba) || - (entry->ending_lba < entry->starting_lba)) - return GPT_ERROR_OUT_OF_REGION; - - /* Entry must not overlap other entries. */ - for (i2 = 0, e2 = entries; i2 < h->number_of_entries; i2++, e2++) { - if (i2 == i || IsUnusedEntry(e2)) - continue; - - if ((entry->starting_lba >= e2->starting_lba) && - (entry->starting_lba <= e2->ending_lba)) - return GPT_ERROR_START_LBA_OVERLAP; - if ((entry->ending_lba >= e2->starting_lba) && - (entry->ending_lba <= e2->ending_lba)) - return GPT_ERROR_END_LBA_OVERLAP; - - /* UniqueGuid field must be unique. */ - if (0 == Memcmp(&entry->unique, &e2->unique, sizeof(Guid))) - return GPT_ERROR_DUP_GUID; - } - } - - /* Success */ - return 0; +int CheckEntries(GptEntry *entries, GptHeader *h) +{ + GptEntry *entry; + uint32_t crc32; + uint32_t i; + + /* Check CRC before examining entries. */ + crc32 = Crc32((const uint8_t *)entries, + h->size_of_entry * h->number_of_entries); + if (crc32 != h->entries_crc32) + return GPT_ERROR_CRC_CORRUPTED; + + /* Check all entries. */ + for (i = 0, entry = entries; i < h->number_of_entries; i++, entry++) { + GptEntry *e2; + uint32_t i2; + + if (IsUnusedEntry(entry)) + continue; + + /* Entry must be in valid region. */ + if ((entry->starting_lba < h->first_usable_lba) || + (entry->ending_lba > h->last_usable_lba) || + (entry->ending_lba < entry->starting_lba)) + return GPT_ERROR_OUT_OF_REGION; + + /* Entry must not overlap other entries. */ + for (i2 = 0, e2 = entries; i2 < h->number_of_entries; + i2++, e2++) { + if (i2 == i || IsUnusedEntry(e2)) + continue; + + if ((entry->starting_lba >= e2->starting_lba) && + (entry->starting_lba <= e2->ending_lba)) + return GPT_ERROR_START_LBA_OVERLAP; + if ((entry->ending_lba >= e2->starting_lba) && + (entry->ending_lba <= e2->ending_lba)) + return GPT_ERROR_END_LBA_OVERLAP; + + /* UniqueGuid field must be unique. */ + if (0 == Memcmp(&entry->unique, &e2->unique, + sizeof(Guid))) + return GPT_ERROR_DUP_GUID; + } + } + + /* Success */ + return 0; } - -/* Returns 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) { - if (Memcmp(h1->signature, h2->signature, sizeof(h1->signature))) - return 1; - if (h1->revision != h2->revision) - return 1; - if (h1->size != h2->size) - return 1; - if (h1->reserved_zero != h2->reserved_zero) - return 1; - if (h1->first_usable_lba != h2->first_usable_lba) - return 1; - if (h1->last_usable_lba != h2->last_usable_lba) - return 1; - if (Memcmp(&h1->disk_uuid, &h2->disk_uuid, sizeof(Guid))) - return 1; - if (h1->number_of_entries != h2->number_of_entries) - return 1; - if (h1->size_of_entry != h2->size_of_entry) - return 1; - if (h1->entries_crc32 != h2->entries_crc32) - return 1; - - return 0; +int HeaderFieldsSame(GptHeader *h1, GptHeader *h2) +{ + if (Memcmp(h1->signature, h2->signature, sizeof(h1->signature))) + return 1; + if (h1->revision != h2->revision) + return 1; + if (h1->size != h2->size) + return 1; + if (h1->reserved_zero != h2->reserved_zero) + return 1; + if (h1->first_usable_lba != h2->first_usable_lba) + return 1; + if (h1->last_usable_lba != h2->last_usable_lba) + return 1; + if (Memcmp(&h1->disk_uuid, &h2->disk_uuid, sizeof(Guid))) + return 1; + if (h1->number_of_entries != h2->number_of_entries) + return 1; + if (h1->size_of_entry != h2->size_of_entry) + return 1; + if (h1->entries_crc32 != h2->entries_crc32) + return 1; + + return 0; } - -int GptSanityCheck(GptData *gpt) { - int retval; - GptHeader* header1 = (GptHeader*)(gpt->primary_header); - GptHeader* header2 = (GptHeader*)(gpt->secondary_header); - GptEntry* entries1 = (GptEntry*)(gpt->primary_entries); - GptEntry* entries2 = (GptEntry*)(gpt->secondary_entries); - GptHeader* goodhdr = NULL; - - gpt->valid_headers = 0; - gpt->valid_entries = 0; - - retval = CheckParameters(gpt); - if (retval != GPT_SUCCESS) - return retval; - - /* Check both headers; we need at least one valid header. */ - if (0 == CheckHeader(header1, 0, gpt->drive_sectors)) { - gpt->valid_headers |= MASK_PRIMARY; - goodhdr = header1; - } - if (0 == CheckHeader(header2, 1, gpt->drive_sectors)) { - gpt->valid_headers |= MASK_SECONDARY; - if (!goodhdr) - goodhdr = header2; - } - - if (!gpt->valid_headers) - return GPT_ERROR_INVALID_HEADERS; - - /* Checks if entries are valid. - * - * Note that we use the same header in both checks. This way we'll - * catch the case where (header1,entries1) and (header2,entries2) - * are both valid, but (entries1 != entries2). */ - if (0 == CheckEntries(entries1, goodhdr)) - gpt->valid_entries |= MASK_PRIMARY; - if (0 == CheckEntries(entries2, goodhdr)) - gpt->valid_entries |= MASK_SECONDARY; - - /* If both headers are good but neither entries were good, check the - * entries with the secondary header. */ - if (MASK_BOTH == gpt->valid_headers && !gpt->valid_entries) { - if (0 == CheckEntries(entries1, header2)) - gpt->valid_entries |= MASK_PRIMARY; - if (0 == CheckEntries(entries2, header2)) - gpt->valid_entries |= MASK_SECONDARY; - if (gpt->valid_entries) { - /* Sure enough, header2 had a good CRC for one of the entries. Mark - * header1 invalid, so we'll update its entries CRC. */ - gpt->valid_headers &= ~MASK_PRIMARY; - goodhdr = header2; - } - } - - if (!gpt->valid_entries) - return GPT_ERROR_INVALID_ENTRIES; - - /* Now that we've determined which header contains a good CRC for - * the entries, make sure the headers are otherwise identical. */ - if (MASK_BOTH == gpt->valid_headers && - 0 != HeaderFieldsSame(header1, header2)) - gpt->valid_headers &= ~MASK_SECONDARY; - - return GPT_SUCCESS; +int GptSanityCheck(GptData *gpt) +{ + int retval; + GptHeader *header1 = (GptHeader *)(gpt->primary_header); + GptHeader *header2 = (GptHeader *)(gpt->secondary_header); + GptEntry *entries1 = (GptEntry *)(gpt->primary_entries); + GptEntry *entries2 = (GptEntry *)(gpt->secondary_entries); + GptHeader *goodhdr = NULL; + + gpt->valid_headers = 0; + gpt->valid_entries = 0; + + retval = CheckParameters(gpt); + if (retval != GPT_SUCCESS) + return retval; + + /* Check both headers; we need at least one valid header. */ + if (0 == CheckHeader(header1, 0, gpt->drive_sectors)) { + gpt->valid_headers |= MASK_PRIMARY; + goodhdr = header1; + } + if (0 == CheckHeader(header2, 1, gpt->drive_sectors)) { + gpt->valid_headers |= MASK_SECONDARY; + if (!goodhdr) + goodhdr = header2; + } + + if (!gpt->valid_headers) + return GPT_ERROR_INVALID_HEADERS; + + /* + * Check if entries are valid. + * + * Note that we use the same header in both checks. This way we'll + * catch the case where (header1,entries1) and (header2,entries2) are + * both valid, but (entries1 != entries2). + */ + if (0 == CheckEntries(entries1, goodhdr)) + gpt->valid_entries |= MASK_PRIMARY; + if (0 == CheckEntries(entries2, goodhdr)) + gpt->valid_entries |= MASK_SECONDARY; + + /* + * If both headers are good but neither entries were good, check the + * entries with the secondary header. + */ + if (MASK_BOTH == gpt->valid_headers && !gpt->valid_entries) { + if (0 == CheckEntries(entries1, header2)) + gpt->valid_entries |= MASK_PRIMARY; + if (0 == CheckEntries(entries2, header2)) + gpt->valid_entries |= MASK_SECONDARY; + if (gpt->valid_entries) { + /* + * Sure enough, header2 had a good CRC for one of the + * entries. Mark header1 invalid, so we'll update its + * entries CRC. + */ + gpt->valid_headers &= ~MASK_PRIMARY; + goodhdr = header2; + } + } + + if (!gpt->valid_entries) + return GPT_ERROR_INVALID_ENTRIES; + + /* + * Now that we've determined which header contains a good CRC for + * the entries, make sure the headers are otherwise identical. + */ + if (MASK_BOTH == gpt->valid_headers && + 0 != HeaderFieldsSame(header1, header2)) + gpt->valid_headers &= ~MASK_SECONDARY; + + return GPT_SUCCESS; } - -void GptRepair(GptData *gpt) { - GptHeader* header1 = (GptHeader*)(gpt->primary_header); - GptHeader* header2 = (GptHeader*)(gpt->secondary_header); - GptEntry* entries1 = (GptEntry*)(gpt->primary_entries); - GptEntry* entries2 = (GptEntry*)(gpt->secondary_entries); - int entries_size; - - /* Need at least one good header and one good set of entries. */ - if (MASK_NONE == gpt->valid_headers || MASK_NONE == gpt->valid_entries) - return; - - /* Repair headers if necessary */ - if (MASK_PRIMARY == gpt->valid_headers) { - /* Primary is good, secondary is bad */ - Memcpy(header2, header1, sizeof(GptHeader)); - header2->my_lba = gpt->drive_sectors - 1; - header2->alternate_lba = 1; - header2->entries_lba = header2->my_lba - GPT_ENTRIES_SECTORS; - header2->header_crc32 = HeaderCrc(header2); - gpt->modified |= GPT_MODIFIED_HEADER2; - } - else if (MASK_SECONDARY == gpt->valid_headers) { - /* Secondary is good, primary is bad */ - Memcpy(header1, header2, sizeof(GptHeader)); - header1->my_lba = 1; - header1->alternate_lba = gpt->drive_sectors - 1; - header1->entries_lba = header1->my_lba + 1; - header1->header_crc32 = HeaderCrc(header1); - gpt->modified |= GPT_MODIFIED_HEADER1; - } - gpt->valid_headers = MASK_BOTH; - - /* Repair entries if necessary */ - entries_size = header1->size_of_entry * header1->number_of_entries; - if (MASK_PRIMARY == gpt->valid_entries) { - /* Primary is good, secondary is bad */ - Memcpy(entries2, entries1, entries_size); - gpt->modified |= GPT_MODIFIED_ENTRIES2; - } - else if (MASK_SECONDARY == gpt->valid_entries) { - /* Secondary is good, primary is bad */ - Memcpy(entries1, entries2, entries_size); - gpt->modified |= GPT_MODIFIED_ENTRIES1; - } - gpt->valid_entries = MASK_BOTH; +void GptRepair(GptData *gpt) +{ + GptHeader *header1 = (GptHeader *)(gpt->primary_header); + GptHeader *header2 = (GptHeader *)(gpt->secondary_header); + GptEntry *entries1 = (GptEntry *)(gpt->primary_entries); + GptEntry *entries2 = (GptEntry *)(gpt->secondary_entries); + int entries_size; + + /* Need at least one good header and one good set of entries. */ + if (MASK_NONE == gpt->valid_headers || MASK_NONE == gpt->valid_entries) + return; + + /* Repair headers if necessary */ + if (MASK_PRIMARY == gpt->valid_headers) { + /* Primary is good, secondary is bad */ + Memcpy(header2, header1, sizeof(GptHeader)); + header2->my_lba = gpt->drive_sectors - 1; + header2->alternate_lba = 1; + header2->entries_lba = header2->my_lba - GPT_ENTRIES_SECTORS; + header2->header_crc32 = HeaderCrc(header2); + gpt->modified |= GPT_MODIFIED_HEADER2; + } + else if (MASK_SECONDARY == gpt->valid_headers) { + /* Secondary is good, primary is bad */ + Memcpy(header1, header2, sizeof(GptHeader)); + header1->my_lba = 1; + header1->alternate_lba = gpt->drive_sectors - 1; + header1->entries_lba = header1->my_lba + 1; + header1->header_crc32 = HeaderCrc(header1); + gpt->modified |= GPT_MODIFIED_HEADER1; + } + gpt->valid_headers = MASK_BOTH; + + /* Repair entries if necessary */ + entries_size = header1->size_of_entry * header1->number_of_entries; + if (MASK_PRIMARY == gpt->valid_entries) { + /* Primary is good, secondary is bad */ + Memcpy(entries2, entries1, entries_size); + gpt->modified |= GPT_MODIFIED_ENTRIES2; + } + else if (MASK_SECONDARY == gpt->valid_entries) { + /* Secondary is good, primary is bad */ + Memcpy(entries1, entries2, entries_size); + gpt->modified |= GPT_MODIFIED_ENTRIES1; + } + gpt->valid_entries = MASK_BOTH; } - -int GetEntrySuccessful(const GptEntry* e) { - return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >> - CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET; +int GetEntrySuccessful(const GptEntry *e) +{ + return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_SUCCESSFUL_MASK) >> + CGPT_ATTRIBUTE_SUCCESSFUL_OFFSET; } - -int GetEntryPriority(const GptEntry* e) { - return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_PRIORITY_MASK) >> - CGPT_ATTRIBUTE_PRIORITY_OFFSET; +int GetEntryPriority(const GptEntry *e) +{ + return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_PRIORITY_MASK) >> + CGPT_ATTRIBUTE_PRIORITY_OFFSET; } - -int GetEntryTries(const GptEntry* e) { - return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_TRIES_MASK) >> - CGPT_ATTRIBUTE_TRIES_OFFSET; +int GetEntryTries(const GptEntry *e) +{ + return (e->attrs.fields.gpt_att & CGPT_ATTRIBUTE_TRIES_MASK) >> + CGPT_ATTRIBUTE_TRIES_OFFSET; } - -void SetEntrySuccessful(GptEntry* e, int successful) { - if (successful) - e->attrs.fields.gpt_att |= CGPT_ATTRIBUTE_SUCCESSFUL_MASK; - else - e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK; +void SetEntrySuccessful(GptEntry *e, int successful) +{ + if (successful) + e->attrs.fields.gpt_att |= CGPT_ATTRIBUTE_SUCCESSFUL_MASK; + else + e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_SUCCESSFUL_MASK; } - -void SetEntryPriority(GptEntry* e, int priority) { - e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_PRIORITY_MASK; - e->attrs.fields.gpt_att |= (priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET) & - CGPT_ATTRIBUTE_PRIORITY_MASK; +void SetEntryPriority(GptEntry *e, int priority) +{ + e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_PRIORITY_MASK; + e->attrs.fields.gpt_att |= + (priority << CGPT_ATTRIBUTE_PRIORITY_OFFSET) & + CGPT_ATTRIBUTE_PRIORITY_MASK; } - -void SetEntryTries(GptEntry* e, int tries) { - e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_TRIES_MASK; - e->attrs.fields.gpt_att |= (tries << CGPT_ATTRIBUTE_TRIES_OFFSET) & - CGPT_ATTRIBUTE_TRIES_MASK; +void SetEntryTries(GptEntry *e, int tries) +{ + e->attrs.fields.gpt_att &= ~CGPT_ATTRIBUTE_TRIES_MASK; + e->attrs.fields.gpt_att |= (tries << CGPT_ATTRIBUTE_TRIES_OFFSET) & + CGPT_ATTRIBUTE_TRIES_MASK; } -void GetCurrentKernelUniqueGuid(GptData *gpt, void *dest) { - GptEntry* entries = (GptEntry*)gpt->primary_entries; - GptEntry* e = entries + gpt->current_kernel; - Memcpy(dest, &e->unique, sizeof(Guid)); +void GetCurrentKernelUniqueGuid(GptData *gpt, void *dest) +{ + GptEntry *entries = (GptEntry *)gpt->primary_entries; + GptEntry *e = entries + gpt->current_kernel; + Memcpy(dest, &e->unique, sizeof(Guid)); } - -const char* GptErrorText(int error_code) +const char *GptErrorText(int error_code) { - switch(error_code) { - case GPT_SUCCESS: - return "none"; + switch(error_code) { + case GPT_SUCCESS: + return "none"; - case GPT_ERROR_NO_VALID_KERNEL: - return "Invalid kernel"; + case GPT_ERROR_NO_VALID_KERNEL: + return "Invalid kernel"; - case GPT_ERROR_INVALID_HEADERS: - return "Invalid headers"; + case GPT_ERROR_INVALID_HEADERS: + return "Invalid headers"; - case GPT_ERROR_INVALID_ENTRIES: - return "Invalid entries"; + case GPT_ERROR_INVALID_ENTRIES: + return "Invalid entries"; - case GPT_ERROR_INVALID_SECTOR_SIZE: - return "Invalid sector size"; + case GPT_ERROR_INVALID_SECTOR_SIZE: + return "Invalid sector size"; - case GPT_ERROR_INVALID_SECTOR_NUMBER: - return "Invalid sector number"; + case GPT_ERROR_INVALID_SECTOR_NUMBER: + return "Invalid sector number"; - case GPT_ERROR_INVALID_UPDATE_TYPE: - return "Invalid update type"; + case GPT_ERROR_INVALID_UPDATE_TYPE: + return "Invalid update type"; - case GPT_ERROR_CRC_CORRUPTED: - return "Entries' crc corrupted"; + case GPT_ERROR_CRC_CORRUPTED: + return "Entries' crc corrupted"; - case GPT_ERROR_OUT_OF_REGION: - return "Entry outside of valid region"; + case GPT_ERROR_OUT_OF_REGION: + return "Entry outside of valid region"; - case GPT_ERROR_START_LBA_OVERLAP: - return "Starting LBA overlaps"; + case GPT_ERROR_START_LBA_OVERLAP: + return "Starting LBA overlaps"; - case GPT_ERROR_END_LBA_OVERLAP: - return "Ending LBA overlaps"; + case GPT_ERROR_END_LBA_OVERLAP: + return "Ending LBA overlaps"; - case GPT_ERROR_DUP_GUID: - return "Duplicated GUID"; + case GPT_ERROR_DUP_GUID: + return "Duplicated GUID"; - default: - break; - }; - return "Unknown"; + default: + break; + }; + return "Unknown"; } diff --git a/firmware/lib/cgptlib/crc32.c b/firmware/lib/cgptlib/crc32.c index 9dacd178..002c5b9a 100755..100644 --- a/firmware/lib/cgptlib/crc32.c +++ b/firmware/lib/cgptlib/crc32.c @@ -42,67 +42,68 @@ #include "crc32.h" static uint32_t crc32_tab[] = { - 0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U, - 0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U, - 0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U, - 0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU, - 0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U, - 0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U, - 0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U, - 0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU, - 0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U, - 0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU, - 0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U, - 0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U, - 0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U, - 0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU, - 0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU, - 0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U, - 0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU, - 0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U, - 0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U, - 0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U, - 0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU, - 0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U, - 0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U, - 0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU, - 0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U, - 0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U, - 0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U, - 0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U, - 0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U, - 0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU, - 0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU, - 0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U, - 0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U, - 0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU, - 0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU, - 0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U, - 0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU, - 0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U, - 0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU, - 0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U, - 0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU, - 0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U, - 0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U, - 0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU, - 0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U, - 0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U, - 0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U, - 0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U, - 0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U, - 0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U, - 0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU, - 0x2d02ef8dU + 0x00000000U, 0x77073096U, 0xee0e612cU, 0x990951baU, 0x076dc419U, + 0x706af48fU, 0xe963a535U, 0x9e6495a3U, 0x0edb8832U, 0x79dcb8a4U, + 0xe0d5e91eU, 0x97d2d988U, 0x09b64c2bU, 0x7eb17cbdU, 0xe7b82d07U, + 0x90bf1d91U, 0x1db71064U, 0x6ab020f2U, 0xf3b97148U, 0x84be41deU, + 0x1adad47dU, 0x6ddde4ebU, 0xf4d4b551U, 0x83d385c7U, 0x136c9856U, + 0x646ba8c0U, 0xfd62f97aU, 0x8a65c9ecU, 0x14015c4fU, 0x63066cd9U, + 0xfa0f3d63U, 0x8d080df5U, 0x3b6e20c8U, 0x4c69105eU, 0xd56041e4U, + 0xa2677172U, 0x3c03e4d1U, 0x4b04d447U, 0xd20d85fdU, 0xa50ab56bU, + 0x35b5a8faU, 0x42b2986cU, 0xdbbbc9d6U, 0xacbcf940U, 0x32d86ce3U, + 0x45df5c75U, 0xdcd60dcfU, 0xabd13d59U, 0x26d930acU, 0x51de003aU, + 0xc8d75180U, 0xbfd06116U, 0x21b4f4b5U, 0x56b3c423U, 0xcfba9599U, + 0xb8bda50fU, 0x2802b89eU, 0x5f058808U, 0xc60cd9b2U, 0xb10be924U, + 0x2f6f7c87U, 0x58684c11U, 0xc1611dabU, 0xb6662d3dU, 0x76dc4190U, + 0x01db7106U, 0x98d220bcU, 0xefd5102aU, 0x71b18589U, 0x06b6b51fU, + 0x9fbfe4a5U, 0xe8b8d433U, 0x7807c9a2U, 0x0f00f934U, 0x9609a88eU, + 0xe10e9818U, 0x7f6a0dbbU, 0x086d3d2dU, 0x91646c97U, 0xe6635c01U, + 0x6b6b51f4U, 0x1c6c6162U, 0x856530d8U, 0xf262004eU, 0x6c0695edU, + 0x1b01a57bU, 0x8208f4c1U, 0xf50fc457U, 0x65b0d9c6U, 0x12b7e950U, + 0x8bbeb8eaU, 0xfcb9887cU, 0x62dd1ddfU, 0x15da2d49U, 0x8cd37cf3U, + 0xfbd44c65U, 0x4db26158U, 0x3ab551ceU, 0xa3bc0074U, 0xd4bb30e2U, + 0x4adfa541U, 0x3dd895d7U, 0xa4d1c46dU, 0xd3d6f4fbU, 0x4369e96aU, + 0x346ed9fcU, 0xad678846U, 0xda60b8d0U, 0x44042d73U, 0x33031de5U, + 0xaa0a4c5fU, 0xdd0d7cc9U, 0x5005713cU, 0x270241aaU, 0xbe0b1010U, + 0xc90c2086U, 0x5768b525U, 0x206f85b3U, 0xb966d409U, 0xce61e49fU, + 0x5edef90eU, 0x29d9c998U, 0xb0d09822U, 0xc7d7a8b4U, 0x59b33d17U, + 0x2eb40d81U, 0xb7bd5c3bU, 0xc0ba6cadU, 0xedb88320U, 0x9abfb3b6U, + 0x03b6e20cU, 0x74b1d29aU, 0xead54739U, 0x9dd277afU, 0x04db2615U, + 0x73dc1683U, 0xe3630b12U, 0x94643b84U, 0x0d6d6a3eU, 0x7a6a5aa8U, + 0xe40ecf0bU, 0x9309ff9dU, 0x0a00ae27U, 0x7d079eb1U, 0xf00f9344U, + 0x8708a3d2U, 0x1e01f268U, 0x6906c2feU, 0xf762575dU, 0x806567cbU, + 0x196c3671U, 0x6e6b06e7U, 0xfed41b76U, 0x89d32be0U, 0x10da7a5aU, + 0x67dd4accU, 0xf9b9df6fU, 0x8ebeeff9U, 0x17b7be43U, 0x60b08ed5U, + 0xd6d6a3e8U, 0xa1d1937eU, 0x38d8c2c4U, 0x4fdff252U, 0xd1bb67f1U, + 0xa6bc5767U, 0x3fb506ddU, 0x48b2364bU, 0xd80d2bdaU, 0xaf0a1b4cU, + 0x36034af6U, 0x41047a60U, 0xdf60efc3U, 0xa867df55U, 0x316e8eefU, + 0x4669be79U, 0xcb61b38cU, 0xbc66831aU, 0x256fd2a0U, 0x5268e236U, + 0xcc0c7795U, 0xbb0b4703U, 0x220216b9U, 0x5505262fU, 0xc5ba3bbeU, + 0xb2bd0b28U, 0x2bb45a92U, 0x5cb36a04U, 0xc2d7ffa7U, 0xb5d0cf31U, + 0x2cd99e8bU, 0x5bdeae1dU, 0x9b64c2b0U, 0xec63f226U, 0x756aa39cU, + 0x026d930aU, 0x9c0906a9U, 0xeb0e363fU, 0x72076785U, 0x05005713U, + 0x95bf4a82U, 0xe2b87a14U, 0x7bb12baeU, 0x0cb61b38U, 0x92d28e9bU, + 0xe5d5be0dU, 0x7cdcefb7U, 0x0bdbdf21U, 0x86d3d2d4U, 0xf1d4e242U, + 0x68ddb3f8U, 0x1fda836eU, 0x81be16cdU, 0xf6b9265bU, 0x6fb077e1U, + 0x18b74777U, 0x88085ae6U, 0xff0f6a70U, 0x66063bcaU, 0x11010b5cU, + 0x8f659effU, 0xf862ae69U, 0x616bffd3U, 0x166ccf45U, 0xa00ae278U, + 0xd70dd2eeU, 0x4e048354U, 0x3903b3c2U, 0xa7672661U, 0xd06016f7U, + 0x4969474dU, 0x3e6e77dbU, 0xaed16a4aU, 0xd9d65adcU, 0x40df0b66U, + 0x37d83bf0U, 0xa9bcae53U, 0xdebb9ec5U, 0x47b2cf7fU, 0x30b5ffe9U, + 0xbdbdf21cU, 0xcabac28aU, 0x53b39330U, 0x24b4a3a6U, 0xbad03605U, + 0xcdd70693U, 0x54de5729U, 0x23d967bfU, 0xb3667a2eU, 0xc4614ab8U, + 0x5d681b02U, 0x2a6f2b94U, 0xb40bbe37U, 0xc30c8ea1U, 0x5a05df1bU, + 0x2d02ef8dU }; -/* Returns a 32-bit CRC of the contents of the buffer. */ -uint32_t Crc32(const void *buffer, uint32_t len) { - uint8_t *byte = (uint8_t*)buffer; - uint32_t i; - uint32_t value = ~0U; - for (i = 0; i < len; ++i) - value = crc32_tab[(value ^ byte[i]) & 0xff] ^ (value >> 8); - return value ^ ~0U; +uint32_t Crc32(const void *buffer, uint32_t len) +{ + uint8_t *byte = (uint8_t *)buffer; + uint32_t i; + uint32_t value = ~0U; + + for (i = 0; i < len; ++i) + value = crc32_tab[(value ^ byte[i]) & 0xff] ^ (value >> 8); + return value ^ ~0U; } diff --git a/firmware/lib/cgptlib/include/cgptlib.h b/firmware/lib/cgptlib/include/cgptlib.h index 6633733e..ccaa2beb 100644 --- a/firmware/lib/cgptlib/include/cgptlib.h +++ b/firmware/lib/cgptlib/include/cgptlib.h @@ -9,20 +9,20 @@ #include "sysincludes.h" enum { - GPT_SUCCESS = 0, - GPT_ERROR_NO_VALID_KERNEL, - GPT_ERROR_INVALID_HEADERS, - GPT_ERROR_INVALID_ENTRIES, - GPT_ERROR_INVALID_SECTOR_SIZE, - GPT_ERROR_INVALID_SECTOR_NUMBER, - GPT_ERROR_INVALID_UPDATE_TYPE, - GPT_ERROR_CRC_CORRUPTED, - GPT_ERROR_OUT_OF_REGION, - GPT_ERROR_START_LBA_OVERLAP, - GPT_ERROR_END_LBA_OVERLAP, - GPT_ERROR_DUP_GUID, - /* Number of errors */ - GPT_ERROR_COUNT + GPT_SUCCESS = 0, + GPT_ERROR_NO_VALID_KERNEL, + GPT_ERROR_INVALID_HEADERS, + GPT_ERROR_INVALID_ENTRIES, + GPT_ERROR_INVALID_SECTOR_SIZE, + GPT_ERROR_INVALID_SECTOR_NUMBER, + GPT_ERROR_INVALID_UPDATE_TYPE, + GPT_ERROR_CRC_CORRUPTED, + GPT_ERROR_OUT_OF_REGION, + 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. */ @@ -31,56 +31,64 @@ enum { #define GPT_MODIFIED_ENTRIES1 0x04 #define GPT_MODIFIED_ENTRIES2 0x08 -#define TOTAL_ENTRIES_SIZE 16384 /* Size of GptData.primary_entries - * and secondary_entries: 128 - * bytes/entry * 128 entries. */ +/* + * Size of GptData.primary_entries and secondary_entries: 128 bytes/entry * 128 + * entries. + */ +#define TOTAL_ENTRIES_SIZE 16384 -/* The 'update_type' of GptUpdateKernelEntry() - * We expose TRY and BAD only because those are what verified boot needs. - * For more precise control on GPT attribute bits, please refer to - * gpt_internal.h */ +/* + * The 'update_type' of GptUpdateKernelEntry(). We expose TRY and BAD only + * because those are what verified boot needs. For more precise control on GPT + * attribute bits, please refer to gpt_internal.h. + */ enum { - GPT_UPDATE_ENTRY_TRY = 1, - /* System will be trying to boot the currently selected kernel partition. - * Update its try count if necessary. */ - GPT_UPDATE_ENTRY_BAD = 2, - /* The currently selected kernel partition failed validation. Mark entry as - * invalid. */ + /* + * System will be trying to boot the currently selected kernel + * partition. Update its try count if necessary. + */ + GPT_UPDATE_ENTRY_TRY = 1, + /* + * The currently selected kernel partition failed validation. Mark + * entry as invalid. + */ + GPT_UPDATE_ENTRY_BAD = 2, }; typedef struct { - /* Fill in the following fields before calling GptInit() */ - uint8_t *primary_header; /* GPT primary header, from sector 1 of disk - * (size: 512 bytes) */ - uint8_t *secondary_header; /* GPT secondary header, from last sector of - * disk (size: 512 bytes) */ - uint8_t *primary_entries; /* primary GPT table, follows primary header - * (size: 16 KB) */ - uint8_t *secondary_entries; /* secondary GPT table, precedes secondary - * header (size: 16 KB) */ - uint32_t sector_bytes; /* Size of a LBA sector, in bytes */ - uint64_t drive_sectors; /* Size of drive in LBA sectors, in sectors */ + /* Fill in the following fields before calling GptInit() */ + /* GPT primary header, from sector 1 of disk (size: 512 bytes) */ + uint8_t *primary_header; + /* GPT secondary header, from last sector of disk (size: 512 bytes) */ + uint8_t *secondary_header; + /* Primary GPT table, follows primary header (size: 16 KB) */ + uint8_t *primary_entries; + /* Secondary GPT table, precedes secondary header (size: 16 KB) */ + uint8_t *secondary_entries; + /* Size of a LBA sector, in bytes */ + uint32_t sector_bytes; + /* Size of drive in LBA sectors, in sectors */ + uint64_t drive_sectors; - /* Outputs */ - uint8_t modified; /* Which inputs have been modified? - * 0x01 = header1 - * 0x02 = header2 - * 0x04 = table1 - * 0x08 = table2 */ - int current_kernel; /* the current chromeos kernel index in partition table. - * -1 means not found on drive. Note that GPT partition - * numbers are traditionally 1-based, but we're using - * a zero-based index here. - */ + /* Outputs */ + /* Which inputs have been modified? GPT_MODIFIED_* */ + uint8_t modified; + /* + * The current chromeos kernel index in partition table. -1 means not + * found on drive. Note that GPT partition numbers are traditionally + * 1-based, but we're using a zero-based index here. + */ + int current_kernel; - /* Internal variables */ - uint32_t valid_headers, valid_entries; - int current_priority; + /* Internal variables */ + uint32_t valid_headers, valid_entries; + int current_priority; } GptData; -int GptInit(GptData* gpt); -/* Initializes the GPT data structure's internal state. The following fields - * must be filled before calling this function: +/** + * Initializes the GPT data structure's internal state. + * + * The following fields must be filled before calling this function: * * primary_header * secondary_header @@ -100,19 +108,23 @@ int GptInit(GptData* gpt); * GPT_ERROR_INVALID_SECTOR_SIZE, size of a sector is not supported, * GPT_ERROR_INVALID_SECTOR_NUMBER, number of sectors in drive is invalid (too * small) */ +int GptInit(GptData *gpt); -int GptNextKernelEntry(GptData* gpt, uint64_t* start_sector, uint64_t* size); -/* Provides the location of the next kernel partition, in order of decreasing - * priority. On return the start_sector parameter contains the LBA sector - * for the start of the kernel partition, and the size parameter contains the - * size of the kernel partition in LBA sectors. gpt.current_kernel contains - * the partition index of the current chromeos kernel partition. +/** + * Provides the location of the next kernel partition, in order of decreasing + * priority. + * + * On return the start_sector parameter contains the LBA sector for the start + * of the kernel partition, and the size parameter contains the size of the + * kernel partition in LBA sectors. gpt.current_kernel contains the partition + * index of the current chromeos kernel partition. * * Returns GPT_SUCCESS if successful, else * GPT_ERROR_NO_VALID_KERNEL, no avaliable kernel, enters recovery mode */ +int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size); -int GptUpdateKernelEntry(GptData* gpt, uint32_t update_type); -/* Updates the kernel entry with the specified index, using the specified type +/** + * Updates the kernel entry with the specified index, using the specified type * of update (GPT_UPDATE_ENTRY_*). * * On return the modified field may be set, if the GPT data has been modified @@ -121,5 +133,6 @@ int GptUpdateKernelEntry(GptData* gpt, uint32_t update_type); * Returns GPT_SUCCESS if successful, else * GPT_ERROR_INVALID_UPDATE_TYPE, invalid 'update_type' is given. */ +int GptUpdateKernelEntry(GptData *gpt, uint32_t update_type); #endif /* VBOOT_REFERENCE_CGPTLIB_H_ */ diff --git a/firmware/lib/cgptlib/include/cgptlib_internal.h b/firmware/lib/cgptlib/include/cgptlib_internal.h index 36e598c2..c7606287 100644 --- a/firmware/lib/cgptlib/include/cgptlib_internal.h +++ b/firmware/lib/cgptlib/include/cgptlib_internal.h @@ -10,13 +10,15 @@ #include "cgptlib.h" #include "gpt.h" -/* If gpt->current_kernel is this value, means either: +/* + * If gpt->current_kernel is this value, means either: * 1. an initial value before scanning GPT entries, * 2. after scanning, no any valid kernel is found. */ #define CGPT_KERNEL_ENTRY_NOT_FOUND (-1) -/* Bit definitions and masks for GPT attributes. +/* + * Bit definitions and masks for GPT attributes. * * 63-61 -- (reserved) * 60 -- read-only @@ -55,80 +57,107 @@ /* Defines GPT sizes */ #define GPT_PMBR_SECTOR 1 /* size (in sectors) of PMBR */ #define GPT_HEADER_SECTOR 1 -#define GPT_ENTRIES_SECTORS 32 /* assume sector size if 512 bytes, then - * (TOTAL_ENTRIES_SIZE / 512) = 32 */ +/* + * Entries sectors assumes sector size if 512 bytes; then (TOTAL_ENTRIES_SIZE / + * 512) = 32 + */ +#define GPT_ENTRIES_SECTORS 32 -/* alias name of index in internal array for primary and secondary header and - * entries. */ +/* + * Alias name of index in internal array for primary and secondary header and + * entries. + */ enum { - /* constants for index */ - PRIMARY = 0, - SECONDARY = 1, - ANY_VALID = 9999, /* accept any between primary and secondary */ - - /* constants for bit mask */ - MASK_NONE = 0, - MASK_PRIMARY = 1, - MASK_SECONDARY = 2, - MASK_BOTH = 3, + /* constants for index */ + PRIMARY = 0, + SECONDARY = 1, + ANY_VALID = 9999, /* accept any between primary and secondary */ + + /* constants for bit mask */ + MASK_NONE = 0, + MASK_PRIMARY = 1, + MASK_SECONDARY = 2, + MASK_BOTH = 3, }; -/* Verify GptData parameters are sane. */ +/** + * Verify GptData parameters are sane. + */ int CheckParameters(GptData* gpt); -/* Check header fields. +/** + * Check header fields. * - * Returns 0 if header is valid, 1 if invalid. */ -int CheckHeader(GptHeader* h, int is_secondary, uint64_t drive_sectors); + * Returns 0 if header is valid, 1 if invalid. + */ +int CheckHeader(GptHeader *h, int is_secondary, uint64_t drive_sectors); -/* Calculate and return the header CRC. */ -uint32_t HeaderCrc(GptHeader* h); +/** + * Calculate and return the header CRC. + */ +uint32_t HeaderCrc(GptHeader *h); -/* Check entries. +/** + * Check entries. * - * Returns 0 if entries are valid, 1 if invalid. */ -int CheckEntries(GptEntry* entries, 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: +/** + * 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 */ + * entries_lba + */ int HeaderFieldsSame(GptHeader *h1, GptHeader *h2); -/* Check GptData, headers, entries. +/** + * Check GptData, headers, entries. * * If successful, sets gpt->valid_headers and gpt->valid_entries and returns * GPT_SUCCESS. * - * On error, returns a GPT_ERROR_* return code. */ -int GptSanityCheck(GptData* gpt); + * On error, returns a GPT_ERROR_* return code. + */ +int GptSanityCheck(GptData *gpt); -/* Repairs GPT data by copying from one set of valid headers/entries to the +/** + * Repair GPT data by copying from one set of valid headers/entries to the * other. Assumes GptSanityCheck() has been run to determine which headers - * and/or entries are already valid. */ -void GptRepair(GptData* gpt); + * and/or entries are already valid. + */ +void GptRepair(GptData *gpt); /* Getters and setters for partition attribute fields. */ -int GetEntrySuccessful(const GptEntry* e); -int GetEntryPriority(const GptEntry* e); -int GetEntryTries(const GptEntry* e); -void SetEntrySuccessful(GptEntry* e, int successful); -void SetEntryPriority(GptEntry* e, int priority); -void SetEntryTries(GptEntry* e, int tries); -/* Return 1 if the entry is unused, 0 if it is used. */ -int IsUnusedEntry(const GptEntry* e); +int GetEntrySuccessful(const GptEntry *e); +int GetEntryPriority(const GptEntry *e); +int GetEntryTries(const GptEntry *e); +void SetEntrySuccessful(GptEntry *e, int successful); +void SetEntryPriority(GptEntry *e, int priority); +void SetEntryTries(GptEntry *e, int tries); -/* Returns 1 if the entry is a Chrome OS kernel partition, else 0. */ -int IsKernelEntry(const GptEntry* e); +/** + * Return 1 if the entry is unused, 0 if it is used. + */ +int IsUnusedEntry(const GptEntry *e); -/* Copies the current kernel partition's UniquePartitionGuid to the dest */ +/** + * Return 1 if the entry is a Chrome OS kernel partition, else 0. + */ +int IsKernelEntry(const GptEntry *e); + +/** + * Copy the current kernel partition's UniquePartitionGuid to the dest. + */ void GetCurrentKernelUniqueGuid(GptData *gpt, void *dest); -/* Returns a pointer to text describing the passed in error */ -const char* GptErrorText(int error_code); +/** + * Return a pointer to text describing the passed in error. + */ +const char *GptErrorText(int error_code); #endif /* VBOOT_REFERENCE_CGPTLIB_INTERNAL_H_ */ diff --git a/firmware/lib/cgptlib/include/gpt.h b/firmware/lib/cgptlib/include/gpt.h index 95b77145..5e60e957 100644 --- a/firmware/lib/cgptlib/include/gpt.h +++ b/firmware/lib/cgptlib/include/gpt.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. * @@ -19,97 +19,97 @@ __pragma(pack(push,1)) /* Support packing for MSVC. */ #define GPT_HEADER_SIGNATURE_SIZE sizeof(GPT_HEADER_SIGNATURE) #define GPT_HEADER_REVISION 0x00010000 -/* The first 3 numbers should be stored in network-endian format - * according to the GUID RFC. The UEFI spec appendix A claims they - * should be stored in little-endian format. But they need to be - * _displayed_ in network-endian format, which is also how they're - * documented in the specs. +/* + * The first 3 numbers should be stored in network-endian format according to + * the GUID RFC. The UEFI spec appendix A claims they should be stored in + * little-endian format. But they need to be _displayed_ in network-endian + * format, which is also how they're documented in the specs. * - * Since what we have here are little-endian constants, they're - * byte-swapped from the normal display order. */ + * Since what we have here are little-endian constants, they're byte-swapped + * from the normal display order. + */ #define GPT_ENT_TYPE_UNUSED \ - {{{0x00000000,0x0000,0x0000,0x00,0x00,{0x00,0x00,0x00,0x00,0x00,0x00}}}} + {{{0x00000000,0x0000,0x0000,0x00,0x00,{0x00,0x00,0x00,0x00,0x00,0x00}}}} #define GPT_ENT_TYPE_EFI \ - {{{0xc12a7328,0xf81f,0x11d2,0xba,0x4b,{0x00,0xa0,0xc9,0x3e,0xc9,0x3b}}}} + {{{0xc12a7328,0xf81f,0x11d2,0xba,0x4b,{0x00,0xa0,0xc9,0x3e,0xc9,0x3b}}}} #define GPT_ENT_TYPE_CHROMEOS_FIRMWARE \ - {{{0xcab6e88e,0xabf3,0x4102,0xa0,0x7a,{0xd4,0xbb,0x9b,0xe3,0xc1,0xd3}}}} + {{{0xcab6e88e,0xabf3,0x4102,0xa0,0x7a,{0xd4,0xbb,0x9b,0xe3,0xc1,0xd3}}}} #define GPT_ENT_TYPE_CHROMEOS_KERNEL \ - {{{0xfe3a2a5d,0x4f32,0x41a7,0xb7,0x25,{0xac,0xcc,0x32,0x85,0xa3,0x09}}}} + {{{0xfe3a2a5d,0x4f32,0x41a7,0xb7,0x25,{0xac,0xcc,0x32,0x85,0xa3,0x09}}}} #define GPT_ENT_TYPE_CHROMEOS_ROOTFS \ - {{{0x3cb8e202,0x3b7e,0x47dd,0x8a,0x3c,{0x7f,0xf2,0xa1,0x3c,0xfc,0xec}}}} + {{{0x3cb8e202,0x3b7e,0x47dd,0x8a,0x3c,{0x7f,0xf2,0xa1,0x3c,0xfc,0xec}}}} #define GPT_ENT_TYPE_CHROMEOS_RESERVED \ - {{{0x2e0a753d,0x9e48,0x43b0,0x83,0x37,{0xb1,0x51,0x92,0xcb,0x1b,0x5e}}}} + {{{0x2e0a753d,0x9e48,0x43b0,0x83,0x37,{0xb1,0x51,0x92,0xcb,0x1b,0x5e}}}} #define GPT_ENT_TYPE_LINUX_DATA \ - {{{0xebd0a0a2,0xb9e5,0x4433,0x87,0xc0,{0x68,0xb6,0xb7,0x26,0x99,0xc7}}}} - + {{{0xebd0a0a2,0xb9e5,0x4433,0x87,0xc0,{0x68,0xb6,0xb7,0x26,0x99,0xc7}}}} #define UUID_NODE_LEN 6 #define GUID_SIZE 16 -/* GUID definition. - * Defined in appendix A of EFI standard. - */ +/* GUID definition. Defined in appendix A of EFI standard. */ typedef struct { - union { - struct { - uint32_t time_low; - uint16_t time_mid; - uint16_t time_high_and_version; - uint8_t clock_seq_high_and_reserved; - uint8_t clock_seq_low; - uint8_t node[UUID_NODE_LEN]; - } Uuid; - uint8_t raw[GUID_SIZE]; - } u; + union { + struct { + uint32_t time_low; + uint16_t time_mid; + uint16_t time_high_and_version; + uint8_t clock_seq_high_and_reserved; + uint8_t clock_seq_low; + uint8_t node[UUID_NODE_LEN]; + } Uuid; + uint8_t raw[GUID_SIZE]; + } u; } __attribute__((packed)) Guid; #define GUID_EXPECTED_SIZE GUID_SIZE -/* GPT header defines how many partitions exist on a drive and sectors managed. - * For every drive device, there are 2 headers, primary and secondary. - * Most of fields are duplicated except my_lba and entries_lba. +/* + * GPT header defines how many partitions exist on a drive and sectors managed. + * For every drive device, there are 2 headers, primary and secondary. Most of + * fields are duplicated except my_lba and entries_lba. * * You may find more details in chapter 5 of EFI standard. */ typedef struct { - char signature[8]; - uint32_t revision; - uint32_t size; - uint32_t header_crc32; - uint32_t reserved_zero; - uint64_t my_lba; - uint64_t alternate_lba; - uint64_t first_usable_lba; - uint64_t last_usable_lba; - Guid disk_uuid; - uint64_t entries_lba; - uint32_t number_of_entries; - uint32_t size_of_entry; - uint32_t entries_crc32; - /* Remainder of sector is reserved and should be 0 */ + char signature[8]; + uint32_t revision; + uint32_t size; + uint32_t header_crc32; + uint32_t reserved_zero; + uint64_t my_lba; + uint64_t alternate_lba; + uint64_t first_usable_lba; + uint64_t last_usable_lba; + Guid disk_uuid; + uint64_t entries_lba; + uint32_t number_of_entries; + uint32_t size_of_entry; + uint32_t entries_crc32; + /* Remainder of sector is reserved and should be 0 */ } __attribute__((packed)) GptHeader; #define GPTHEADER_EXPECTED_SIZE 92 -/* GPT partition entry defines the starting and ending LBAs of a partition. - * It also contains the unique GUID, type, and attribute bits. +/* + * GPT partition entry defines the starting and ending LBAs of a partition. It + * also contains the unique GUID, type, and attribute bits. * * You may find more details in chapter 5 of EFI standard. */ typedef struct { - Guid type; - Guid unique; - uint64_t starting_lba; - uint64_t ending_lba; - union { - struct { - uint16_t reserved[3]; - uint16_t gpt_att; - } __attribute__((packed)) fields; - uint64_t whole; - } attrs; - uint16_t name[36]; /* UTF-16 encoded partition name */ - /* Remainder of entry is reserved and should be 0 */ + Guid type; + Guid unique; + uint64_t starting_lba; + uint64_t ending_lba; + union { + struct { + uint16_t reserved[3]; + uint16_t gpt_att; + } __attribute__((packed)) fields; + uint64_t whole; + } attrs; + uint16_t name[36]; /* UTF-16 encoded partition name */ + /* Remainder of entry is reserved and should be 0 */ } __attribute__((packed)) GptEntry; #define GPTENTRY_EXPECTED_SIZE 128 diff --git a/firmware/lib/crc8.c b/firmware/lib/crc8.c index fa770253..b0ee8679 100644 --- a/firmware/lib/crc8.c +++ b/firmware/lib/crc8.c @@ -1,28 +1,28 @@ -/* 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. */ #include "crc8.h" -/* Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial. A - * table-based algorithm would be faster, but for only a few bytes it isn't - * worth the code size. */ -uint8_t Crc8(const void* vptr, int len) { - const uint8_t *data = vptr; - unsigned crc = 0; - int i, j; +/** + * Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial. A table-based + * algorithm would be faster, but for only a few bytes it isn't worth the code + * size. */ +uint8_t Crc8(const void *vptr, int len) +{ + const uint8_t *data = vptr; + unsigned crc = 0; + int i, j; - for (j = len; j; j--, data++) { - crc ^= (*data << 8); - for(i = 8; i; i--) { - if (crc & 0x8000) - crc ^= (0x1070 << 3); - crc <<= 1; - } - } + for (j = len; j; j--, data++) { + crc ^= (*data << 8); + for(i = 8; i; i--) { + if (crc & 0x8000) + crc ^= (0x1070 << 3); + crc <<= 1; + } + } - return (uint8_t)(crc >> 8); + return (uint8_t)(crc >> 8); } - - diff --git a/firmware/lib/include/vboot_display.h b/firmware/lib/include/vboot_display.h index f8aa810b..52730b9e 100644 --- a/firmware/lib/include/vboot_display.h +++ b/firmware/lib/include/vboot_display.h @@ -8,6 +8,7 @@ #ifndef VBOOT_REFERENCE_VBOOT_DISPLAY_H_ #define VBOOT_REFERENCE_VBOOT_DISPLAY_H_ +#include "bmpblk_font.h" #include "vboot_api.h" #include "vboot_nvstorage.h" @@ -20,7 +21,36 @@ VbError_t VbCheckDisplayKey(VbCommonParams *cparams, uint32_t key, VbNvContext *vncptr); /* Internal functions, for unit testing */ + +typedef FontArrayHeader VbFont_t; + +VbFont_t *VbInternalizeFontData(FontArrayHeader *fonthdr); + +void VbDoneWithFontForNow(VbFont_t *ptr); + +ImageInfo *VbFindFontGlyph(VbFont_t *font, uint32_t ascii, + void **bufferptr, uint32_t *buffersize); + +/** + * Try to display the specified text at a particular position. + */ +void VbRenderTextAtPos(char *text, int right_to_left, + uint32_t x, uint32_t y, VbFont_t *font); + +/** + * Return a description of the recovery reason code. + */ const char *RecoveryReasonString(uint8_t code); +/** + * Return a fixed string representing the HWID. + */ +char *VbHWID(VbCommonParams *cparams); + +/** + * Get the number of localizations in the GBB bitmap data. + */ +VbError_t VbGetLocalizationCount(VbCommonParams *cparams, uint32_t *count); + #endif /* VBOOT_REFERENCE_VBOOT_DISPLAY_H_ */ diff --git a/firmware/lib/include/vboot_kernel.h b/firmware/lib/include/vboot_kernel.h index 6e6c5dc3..e3a44190 100644 --- a/firmware/lib/include/vboot_kernel.h +++ b/firmware/lib/include/vboot_kernel.h @@ -10,7 +10,9 @@ #define VBOOT_REFERENCE_VBOOT_KERNEL_H_ #include "cgptlib.h" +#include "load_firmware_fw.h" #include "vboot_api.h" +#include "vboot_kernel.h" /** * Allocate and read GPT data from the drive. The sector_bytes and @@ -26,4 +28,47 @@ int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata); */ int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata); +/** + * Accessors for unit tests only. + */ +VbNvContext *VbApiKernelGetVnc(void); + +/** + * Try to load a kernel. + */ +uint32_t VbTryLoadKernel(VbCommonParams *cparams, LoadKernelParams *p, + uint32_t get_info_flags); + +/** + * Ask the user to confirm something. + * + * We should display whatever the question is first, then call this. ESC is + * always "no", ENTER is always "yes", and we'll specify what SPACE means. We + * don't return until one of those keys is pressed, or until asked to shut + * down. + * + * Returns: 1=yes, 0=no, -1 = shutdown. + */ +int VbUserConfirms(VbCommonParams *cparams, int space_means_no); + +/** + * Handle a normal boot. + */ +VbError_t VbBootNormal(VbCommonParams *cparams, LoadKernelParams *p); + +/** + * Handle a developer-mode boot. + */ +VbError_t VbBootDeveloper(VbCommonParams *cparams, LoadKernelParams *p); + +/** + * Handle a recovery-mode boot. + */ +VbError_t VbBootRecovery(VbCommonParams *cparams, LoadKernelParams *p); + +/** + * Sync EC firmware to expected version. + */ +VbError_t VbEcSoftwareSync(VbCommonParams *cparams); + #endif /* VBOOT_REFERENCE_VBOOT_KERNEL_H_ */ diff --git a/firmware/lib/rollback_index.c b/firmware/lib/rollback_index.c index 82323235..a7b65510 100644 --- a/firmware/lib/rollback_index.c +++ b/firmware/lib/rollback_index.c @@ -18,9 +18,11 @@ #endif #ifdef FOR_TEST -/* Compiling for unit test, so we need the real implementations of +/* + * Compiling for unit test, so we need the real implementations of * rollback functions. The unit test mocks the underlying tlcl - * functions, so this is ok to run on the host. */ + * functions, so this is ok to run on the host. + */ #undef CHROMEOS_ENVIRONMENT #undef DISABLE_ROLLBACK_TPM #endif @@ -30,13 +32,14 @@ static int g_rollback_recovery_mode = 0; /* disable MSVC warning on const logical expression (as in } while(0);) */ __pragma(warning (disable: 4127)) -#define RETURN_ON_FAILURE(tpm_command) do { \ - uint32_t result; \ - if ((result = (tpm_command)) != TPM_SUCCESS) { \ - VBDEBUG(("Rollback: %08x returned by " #tpm_command "\n", (int)result)); \ - return result; \ - } \ - } while (0) +#define RETURN_ON_FAILURE(tpm_command) do { \ + uint32_t result; \ + if ((result = (tpm_command)) != TPM_SUCCESS) { \ + VBDEBUG(("Rollback: %08x returned by " #tpm_command \ + "\n", (int)result)); \ + return result; \ + } \ + } while (0) uint32_t TPMClearAndReenable(void) diff --git a/firmware/lib/stateful_util.c b/firmware/lib/stateful_util.c index 01a7d641..6db03fc5 100644 --- a/firmware/lib/stateful_util.c +++ b/firmware/lib/stateful_util.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. * @@ -6,65 +6,67 @@ */ #include "stateful_util.h" - #include "utility.h" -void StatefulInit(MemcpyState* state, void* buf, uint64_t len) { - state->remaining_buf = buf; - state->remaining_len = len; - state->overrun = 0; +void StatefulInit(MemcpyState *state, void *buf, uint64_t len) +{ + state->remaining_buf = buf; + state->remaining_len = len; + state->overrun = 0; } -void* StatefulSkip(MemcpyState* state, uint64_t len) { - if (state->overrun) - return NULL; - if (len > state->remaining_len) { - state->overrun = 1; - return NULL; - } - state->remaining_buf += len; - state->remaining_len -= len; - return state; // have to return something non-NULL +void *StatefulSkip(MemcpyState *state, uint64_t len) +{ + if (state->overrun) + return NULL; + if (len > state->remaining_len) { + state->overrun = 1; + return NULL; + } + state->remaining_buf += len; + state->remaining_len -= len; + return state; /* Must return something non-NULL. */ } -void* StatefulMemcpy(MemcpyState* state, void* dst, - uint64_t len) { - if (state->overrun) - return NULL; - if (len > state->remaining_len) { - state->overrun = 1; - return NULL; - } - Memcpy(dst, state->remaining_buf, len); - state->remaining_buf += len; - state->remaining_len -= len; - return dst; +void *StatefulMemcpy(MemcpyState *state, void *dst, uint64_t len) +{ + if (state->overrun) + return NULL; + if (len > state->remaining_len) { + state->overrun = 1; + return NULL; + } + Memcpy(dst, state->remaining_buf, len); + state->remaining_buf += len; + state->remaining_len -= len; + return dst; } -const void* StatefulMemcpy_r(MemcpyState* state, const void* src, - uint64_t len) { - if (state->overrun) - return NULL; - if (len > state->remaining_len) { - state->overrun = 1; - return NULL; - } - Memcpy(state->remaining_buf, src, len); - state->remaining_buf += len; - state->remaining_len -= len; - return src; +const void *StatefulMemcpy_r(MemcpyState *state, const void *src, uint64_t len) +{ + if (state->overrun) + return NULL; + if (len > state->remaining_len) { + state->overrun = 1; + return NULL; + } + Memcpy(state->remaining_buf, src, len); + state->remaining_buf += len; + state->remaining_len -= len; + return src; } -const void* StatefulMemset_r(MemcpyState* state, const uint8_t val, - uint64_t len) { - if (state->overrun) - return NULL; - if (len > state->remaining_len) { - state->overrun = 1; - return NULL; - } - Memset(state->remaining_buf, val, len); - state->remaining_buf += len; - state->remaining_len -= len; - return state; // have to return something non-NULL +const void *StatefulMemset_r(MemcpyState *state, const uint8_t val, + uint64_t len) +{ + if (state->overrun) + return NULL; + if (len > state->remaining_len) { + state->overrun = 1; + return NULL; + } + Memset(state->remaining_buf, val, len); + state->remaining_buf += len; + state->remaining_len -= len; + return state; /* Must return something non-NULL. */ } diff --git a/firmware/lib/tpm_bootmode.c b/firmware/lib/tpm_bootmode.c index 0e0e084b..ec8fac7e 100644 --- a/firmware/lib/tpm_bootmode.c +++ b/firmware/lib/tpm_bootmode.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. * @@ -14,7 +14,8 @@ /* TPM PCR to use for storing boot mode measurements. */ #define BOOT_MODE_PCR 0 -/* Input digests for PCR extend. +/* + * Input digests for PCR extend. * These are calculated as: * SHA1("|Developer_Mode||Recovery_Mode||Keyblock_Mode|"). * Developer_Mode can be 0 or 1. @@ -31,105 +32,125 @@ */ const char* kBootStateSHA1Digests[] = { - /* SHA1("\x00\x00\x00") */ - "\x29\xe2\xdc\xfb\xb1\x6f\x63\xbb\x02\x54\xdf\x75\x85\xa1\x5b\xb6" - "\xfb\x5e\x92\x7d", + /* SHA1("\x00\x00\x00") */ + "\x29\xe2\xdc\xfb\xb1\x6f\x63\xbb\x02\x54\xdf\x75\x85\xa1\x5b\xb6" + "\xfb\x5e\x92\x7d", - /* SHA1("\x00\x00\x01") */ - "\x25\x47\xcc\x73\x6e\x95\x1f\xa4\x91\x98\x53\xc4\x3a\xe8\x90\x86" - "\x1a\x3b\x32\x64", + /* SHA1("\x00\x00\x01") */ + "\x25\x47\xcc\x73\x6e\x95\x1f\xa4\x91\x98\x53\xc4\x3a\xe8\x90\x86" + "\x1a\x3b\x32\x64", - /* SHA1("\x00\x00\x02") */ - "\x1e\xf6\x24\x48\x2d\x62\x0e\x43\xe6\xd3\x4d\xa1\xaf\xe4\x62\x67" - "\xfc\x69\x5d\x9b", + /* SHA1("\x00\x00\x02") */ + "\x1e\xf6\x24\x48\x2d\x62\x0e\x43\xe6\xd3\x4d\xa1\xaf\xe4\x62\x67" + "\xfc\x69\x5d\x9b", - /* SHA1("\x00\x01\x00") */ - "\x62\x57\x18\x91\x21\x5b\x4e\xfc\x1c\xea\xb7\x44\xce\x59\xdd\x0b" - "\x66\xea\x6f\x73", + /* SHA1("\x00\x01\x00") */ + "\x62\x57\x18\x91\x21\x5b\x4e\xfc\x1c\xea\xb7\x44\xce\x59\xdd\x0b" + "\x66\xea\x6f\x73", - /* SHA1("\x00\x01\x01") */ - "\xee\xe4\x47\xed\xc7\x9f\xea\x1c\xa7\xc7\xd3\x4e\x46\x32\x61\xcd" - "\xa4\xba\x33\x9e", + /* SHA1("\x00\x01\x01") */ + "\xee\xe4\x47\xed\xc7\x9f\xea\x1c\xa7\xc7\xd3\x4e\x46\x32\x61\xcd" + "\xa4\xba\x33\x9e", - /* SHA1("\x00\x01\x02") */ - "\x0c\x7a\x62\x3f\xd2\xbb\xc0\x5b\x06\x42\x3b\xe3\x59\xe4\x02\x1d" - "\x36\xe7\x21\xad", + /* SHA1("\x00\x01\x02") */ + "\x0c\x7a\x62\x3f\xd2\xbb\xc0\x5b\x06\x42\x3b\xe3\x59\xe4\x02\x1d" + "\x36\xe7\x21\xad", - /* SHA1("\x01\x00\x00") */ - "\x95\x08\xe9\x05\x48\xb0\x44\x0a\x4a\x61\xe5\x74\x3b\x76\xc1\xe3" - "\x09\xb2\x3b\x7f", + /* SHA1("\x01\x00\x00") */ + "\x95\x08\xe9\x05\x48\xb0\x44\x0a\x4a\x61\xe5\x74\x3b\x76\xc1\xe3" + "\x09\xb2\x3b\x7f", - /* SHA1("\x01\x00\x01") */ - "\xc4\x2a\xc1\xc4\x6f\x1d\x4e\x21\x1c\x73\x5c\xc7\xdf\xad\x4f\xf8" - "\x39\x11\x10\xe9", + /* SHA1("\x01\x00\x01") */ + "\xc4\x2a\xc1\xc4\x6f\x1d\x4e\x21\x1c\x73\x5c\xc7\xdf\xad\x4f\xf8" + "\x39\x11\x10\xe9", - /* SHA1("\x01\x00\x02") */ - "\xfa\x01\x0d\x26\x64\xcc\x5b\x3b\x82\xee\x48\x8f\xe2\xb9\xf5\x0f" - "\x49\x32\xeb\x8f", + /* SHA1("\x01\x00\x02") */ + "\xfa\x01\x0d\x26\x64\xcc\x5b\x3b\x82\xee\x48\x8f\xe2\xb9\xf5\x0f" + "\x49\x32\xeb\x8f", - /* SHA1("\x01\x01\x00") */ - "\x47\xec\x8d\x98\x36\x64\x33\xdc\x00\x2e\x77\x21\xc9\xe3\x7d\x50" - "\x67\x54\x79\x37", + /* SHA1("\x01\x01\x00") */ + "\x47\xec\x8d\x98\x36\x64\x33\xdc\x00\x2e\x77\x21\xc9\xe3\x7d\x50" + "\x67\x54\x79\x37", - /* SHA1("\x01\x01\x01") */ - "\x28\xd8\x6c\x56\xb3\xbf\x26\xd2\x36\x56\x9b\x8d\xc8\xc3\xf9\x1f" - "\x32\xf4\x7b\xc7", + /* SHA1("\x01\x01\x01") */ + "\x28\xd8\x6c\x56\xb3\xbf\x26\xd2\x36\x56\x9b\x8d\xc8\xc3\xf9\x1f" + "\x32\xf4\x7b\xc7", - /* SHA1("\x01\x01\x02") */ - "\x12\xa3\x40\xd7\x89\x7f\xe7\x13\xfc\x8f\x02\xac\x53\x65\xb8\x6e" - "\xbf\x35\x31\x78", + /* SHA1("\x01\x01\x02") */ + "\x12\xa3\x40\xd7\x89\x7f\xe7\x13\xfc\x8f\x02\xac\x53\x65\xb8\x6e" + "\xbf\x35\x31\x78", }; -#define MAX_BOOT_STATE_INDEX (sizeof(kBootStateSHA1Digests)/sizeof(char*)) +#define MAX_BOOT_STATE_INDEX (sizeof(kBootStateSHA1Digests)/sizeof(char *)) -/* Used for PCR extend when the passed-in boot state is invalid or - * if there is an internal error. */ +/* + * Used for PCR extend when the passed-in boot state is invalid or if there is + * an internal error. + */ const uint8_t kBootInvalidSHA1Digest[] = { - "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" - "\xff\xff\xff\xff" + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff\xff" }; -/* Given the boot state, return the correct SHA1 digest index for TPMExtend - * in kBootStateSHA1Digests[]. */ -int GetBootStateIndex(int dev_mode, int rec_mode, uint64_t keyblock_flags) { - int index = 0; - - /* Convert keyblock flags into keyblock mode which we use to index into - * kBootStateSHA1Digest[]. */ - switch(keyblock_flags) { - case 6: /* KEY_BLOCK_FLAG_RECOVERY_0 | KEY_BLOCK_FLAG_DEVELOPER_1 */ - /* Developer firmware. */ - index = 2; - break; - case 7: /* KEY_BLOCK_FLAG_RECOVERY_0 | KEY_BLOCK_FLAG_DEVELOPER_0 - * | KEY_BLOCK_FLAGS_DEVELOPER_1 */ - index = 1; - break; - default: - index = 0; /* Any other keyblock flags. */ - }; - - if (rec_mode) - index += 3; - if (dev_mode) - index += 6; - return index; +/** + * Given the boot state, return the correct SHA1 digest index for TPMExtend + * in kBootStateSHA1Digests[]. + */ +int GetBootStateIndex(int dev_mode, int rec_mode, uint64_t keyblock_flags) +{ + int index = 0; + + /* + * Convert keyblock flags into keyblock mode which we use to index into + * kBootStateSHA1Digest[]. + */ + switch(keyblock_flags) { + case 6: + /* + * KEY_BLOCK_FLAG_RECOVERY_0 | KEY_BLOCK_FLAG_DEVELOPER_1 + * + * Developer firmware. */ + index = 2; + break; + case 7: + /* + * KEY_BLOCK_FLAG_RECOVERY_0 | KEY_BLOCK_FLAG_DEVELOPER_0 + * | KEY_BLOCK_FLAGS_DEVELOPER_1 + */ + index = 1; + break; + default: + /* Any other keyblock flags. */ + index = 0; + }; + + if (rec_mode) + index += 3; + if (dev_mode) + index += 6; + return index; } uint32_t SetTPMBootModeState(int developer_mode, int recovery_mode, - uint64_t fw_keyblock_flags) { - uint32_t result; - const uint8_t* in_digest = NULL; - uint8_t out_digest[20]; /* For PCR extend output. */ - int digest_index = GetBootStateIndex(developer_mode, recovery_mode, - fw_keyblock_flags); - - if (digest_index >= 0 && digest_index < MAX_BOOT_STATE_INDEX) - in_digest = (const uint8_t*)kBootStateSHA1Digests[digest_index]; - else - in_digest = kBootInvalidSHA1Digest; /* Internal out of bounds error. */ - result = TlclExtend(BOOT_MODE_PCR, in_digest, out_digest); - VBDEBUG(("TPM: SetTPMBootModeState boot mode PCR out_digest %02x %02x %02x " - "%02x\n", out_digest, out_digest+1, out_digest+2, out_digest+3)); - return result; + uint64_t fw_keyblock_flags) +{ + uint32_t result; + const uint8_t *in_digest = NULL; + uint8_t out_digest[20]; /* For PCR extend output. */ + int digest_index = GetBootStateIndex(developer_mode, recovery_mode, + fw_keyblock_flags); + + if (digest_index >= 0 && digest_index < MAX_BOOT_STATE_INDEX) { + in_digest = (const uint8_t*) + kBootStateSHA1Digests[digest_index]; + } else { + /* Internal out of bounds error. */ + in_digest = kBootInvalidSHA1Digest; + } + + result = TlclExtend(BOOT_MODE_PCR, in_digest, out_digest); + VBDEBUG(("TPM: SetTPMBootModeState boot mode PCR out_digest " + "%02x %02x %02x %02x\n", + out_digest, out_digest+1, out_digest+2, out_digest+3)); + return result; } diff --git a/firmware/lib/tpm_lite/tlcl.c b/firmware/lib/tpm_lite/tlcl.c index ce5614e1..7acca9a1 100644 --- a/firmware/lib/tpm_lite/tlcl.c +++ b/firmware/lib/tpm_lite/tlcl.c @@ -21,6 +21,11 @@ #include "utility.h" #include "vboot_api.h" +#ifdef FOR_TEST +/* Allow unit testing implementation of TlclSendReceive() */ +#undef CHROMEOS_ENVIRONMENT +#endif + /* Sets the size field of a TPM command. */ static INLINE void SetTpmCommandSize(uint8_t* buffer, uint32_t size) { ToTpmUint32(buffer + sizeof(uint16_t), size); diff --git a/firmware/lib/utility.c b/firmware/lib/utility.c index 5e506f33..66b8eff3 100644 --- a/firmware/lib/utility.c +++ b/firmware/lib/utility.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. * @@ -8,18 +8,20 @@ #include "sysincludes.h" #include "utility.h" -int SafeMemcmp(const void* s1, const void* s2, size_t n) { - const unsigned char* us1 = s1; - const unsigned char* us2 = s2; - int result = 0; +int SafeMemcmp(const void *s1, const void *s2, size_t n) { + const unsigned char *us1 = s1; + const unsigned char *us2 = s2; + int result = 0; - if (0 == n) - return 0; + if (0 == n) + return 0; - /* Code snippet without data-dependent branch due to - * Nate Lawson (nate@root.org) of Root Labs. */ - while (n--) - result |= *us1++ ^ *us2++; + /* + * Code snippet without data-dependent branch due to Nate Lawson + * (nate@root.org) of Root Labs. + */ + while (n--) + result |= *us1++ ^ *us2++; - return result != 0; + return result != 0; } diff --git a/firmware/lib/utility_string.c b/firmware/lib/utility_string.c index 424c3e33..b1b1a391 100644 --- a/firmware/lib/utility_string.c +++ b/firmware/lib/utility_string.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. * @@ -10,62 +10,64 @@ uint32_t Uint64ToString(char *buf, uint32_t bufsize, uint64_t value, - uint32_t radix, uint32_t zero_pad_width) { - char ibuf[UINT64_TO_STRING_MAX]; - char *s; - uint32_t usedsize = 1; - - if (!buf) - return 0; - - /* Clear output buffer in case of error */ - *buf = '\0'; - - /* Sanity-check input args */ - if (radix < 2 || radix > 36 || zero_pad_width >= UINT64_TO_STRING_MAX) - return 0; - - /* Start at end of string and work backwards */ - s = ibuf + UINT64_TO_STRING_MAX - 1; - *(s) = '\0'; - do { - int v = value % radix; - value /= radix; - - *(--s) = (char)(v < 10 ? v + '0' : v + 'a' - 10); - if (++usedsize > bufsize) - return 0; /* Result won't fit in buffer */ - } while (value); - - /* Zero-pad if necessary */ - while (usedsize <= zero_pad_width) { - *(--s) = '0'; - if (++usedsize > bufsize) - return 0; /* Result won't fit in buffer */ - } - - /* Now copy the string back to the input buffer. */ - Memcpy(buf, s, usedsize); - - /* Don't count the terminating null in the bytes used */ - return usedsize - 1; + uint32_t radix, uint32_t zero_pad_width) +{ + char ibuf[UINT64_TO_STRING_MAX]; + char *s; + uint32_t usedsize = 1; + + if (!buf) + return 0; + + /* Clear output buffer in case of error */ + *buf = '\0'; + + /* Sanity-check input args */ + if (radix < 2 || radix > 36 || zero_pad_width >= UINT64_TO_STRING_MAX) + return 0; + + /* Start at end of string and work backwards */ + s = ibuf + UINT64_TO_STRING_MAX - 1; + *(s) = '\0'; + do { + int v = value % radix; + value /= radix; + + *(--s) = (char)(v < 10 ? v + '0' : v + 'a' - 10); + if (++usedsize > bufsize) + return 0; /* Result won't fit in buffer */ + } while (value); + + /* Zero-pad if necessary */ + while (usedsize <= zero_pad_width) { + *(--s) = '0'; + if (++usedsize > bufsize) + return 0; /* Result won't fit in buffer */ + } + + /* Now copy the string back to the input buffer. */ + Memcpy(buf, s, usedsize); + + /* Don't count the terminating null in the bytes used */ + return usedsize - 1; } +uint32_t Strncat(char *dest, const char *src, uint32_t destlen) +{ + uint32_t used = 0; -uint32_t Strncat(char *dest, const char *src, uint32_t destlen) { - uint32_t used = 0; + if (!dest || !src) + return 0; - if (!dest || !src) - return 0; + /* Skip past existing string in destination.*/ + while (dest[used] && used < destlen - 1) + used++; - /* Skip past existing string in destination.*/ - while (dest[used] && used < destlen - 1) - used++; - /* Now copy source */ - while (*src && used < destlen - 1) - dest[used++] = *src++; + /* Now copy source */ + while (*src && used < destlen - 1) + dest[used++] = *src++; - /* Terminate destination and return count of non-null characters */ - dest[used] = 0; - return used; + /* Terminate destination and return count of non-null characters */ + dest[used] = 0; + return used; } diff --git a/firmware/lib/vboot_api_firmware.c b/firmware/lib/vboot_api_firmware.c index 1426c9ab..0d0c2262 100644 --- a/firmware/lib/vboot_api_firmware.c +++ b/firmware/lib/vboot_api_firmware.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. * @@ -14,99 +14,111 @@ #include "vboot_common.h" #include "vboot_nvstorage.h" -VbError_t VbSelectFirmware(VbCommonParams* cparams, - VbSelectFirmwareParams* fparams) { - VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob; - VbNvContext vnc; - VbError_t retval = VBERROR_UNKNOWN; /* Assume error until proven successful */ - int is_rec = (shared->recovery_reason ? 1 : 0); - int is_dev = (shared->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0); - uint32_t tpm_status = 0; - - /* Start timer */ - shared->timer_vb_select_firmware_enter = VbExGetTimer(); - - /* Load NV storage */ - VbExNvStorageRead(vnc.raw); - VbNvSetup(&vnc); - - if (is_rec) { - /* Recovery is requested; go straight to recovery without checking the - * RW firmware. */ - VBDEBUG(("VbSelectFirmware() detected recovery request\n")); - - /* Go directly to recovery mode */ - fparams->selected_firmware = VB_SELECT_FIRMWARE_RECOVERY; - - } else { - /* Chain to LoadFirmware() */ - retval = LoadFirmware(cparams, fparams, &vnc); - - /* Exit if we failed to find an acceptable firmware */ - if (VBERROR_SUCCESS != retval) - goto VbSelectFirmware_exit; - - /* Translate the selected firmware path */ - if (shared->flags & VBSD_LF_USE_RO_NORMAL) { - /* Request the read-only normal/dev code path */ - fparams->selected_firmware = VB_SELECT_FIRMWARE_READONLY; - } else if (0 == shared->firmware_index) - fparams->selected_firmware = VB_SELECT_FIRMWARE_A; - else - fparams->selected_firmware = VB_SELECT_FIRMWARE_B; - - /* Update TPM if necessary */ - if (shared->fw_version_tpm_start < shared->fw_version_tpm) { - VBPERFSTART("VB_TPMU"); - tpm_status = RollbackFirmwareWrite(shared->fw_version_tpm); - VBPERFEND("VB_TPMU"); - if (0 != tpm_status) { - VBDEBUG(("Unable to write firmware version to TPM.\n")); - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_W_ERROR); - retval = VBERROR_TPM_WRITE_FIRMWARE; - goto VbSelectFirmware_exit; - } - } - - /* Lock firmware versions in TPM */ - VBPERFSTART("VB_TPML"); - tpm_status = RollbackFirmwareLock(); - VBPERFEND("VB_TPML"); - if (0 != tpm_status) { - VBDEBUG(("Unable to lock firmware version in TPM.\n")); - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_L_ERROR); - retval = VBERROR_TPM_LOCK_FIRMWARE; - goto VbSelectFirmware_exit; - } - } - - /* At this point, we have a good idea of how we are going to - * boot. Update the TPM with this state information. */ - tpm_status = SetTPMBootModeState(is_dev, is_rec, shared->fw_keyblock_flags); - if (0 != tpm_status) { - VBDEBUG(("Unable to update the TPM with boot mode information.\n")); - if (!is_rec) { - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_U_ERROR); - retval = VBERROR_TPM_SET_BOOT_MODE_STATE; - goto VbSelectFirmware_exit; - } - } - - /* Success! */ - retval = VBERROR_SUCCESS; - -VbSelectFirmware_exit: - - /* Save NV storage */ - VbNvTeardown(&vnc); - if (vnc.raw_changed) - VbExNvStorageWrite(vnc.raw); - - /* Stop timer */ - shared->timer_vb_select_firmware_exit = VbExGetTimer(); - - /* Should always have a known error code */ - VbAssert(VBERROR_UNKNOWN != retval); - - return retval; +VbError_t VbSelectFirmware(VbCommonParams *cparams, + VbSelectFirmwareParams *fparams) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + VbNvContext vnc; + VbError_t retval = VBERROR_UNKNOWN; /* Default to error */ + int is_rec = (shared->recovery_reason ? 1 : 0); + int is_dev = (shared->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0); + uint32_t tpm_status = 0; + + /* Start timer */ + shared->timer_vb_select_firmware_enter = VbExGetTimer(); + + /* Load NV storage */ + VbExNvStorageRead(vnc.raw); + VbNvSetup(&vnc); + + if (is_rec) { + /* + * Recovery is requested; go straight to recovery without + * checking the RW firmware. + */ + VBDEBUG(("VbSelectFirmware() detected recovery request\n")); + + /* Go directly to recovery mode */ + fparams->selected_firmware = VB_SELECT_FIRMWARE_RECOVERY; + } else { + /* Chain to LoadFirmware() */ + retval = LoadFirmware(cparams, fparams, &vnc); + + /* Exit if we failed to find an acceptable firmware */ + if (VBERROR_SUCCESS != retval) + goto VbSelectFirmware_exit; + + /* Translate the selected firmware path */ + if (shared->flags & VBSD_LF_USE_RO_NORMAL) { + /* Request the read-only normal/dev code path */ + fparams->selected_firmware = + VB_SELECT_FIRMWARE_READONLY; + } else if (0 == shared->firmware_index) + fparams->selected_firmware = VB_SELECT_FIRMWARE_A; + else { + fparams->selected_firmware = VB_SELECT_FIRMWARE_B; + } + + /* Update TPM if necessary */ + if (shared->fw_version_tpm_start < shared->fw_version_tpm) { + VBPERFSTART("VB_TPMU"); + tpm_status = + RollbackFirmwareWrite(shared->fw_version_tpm); + VBPERFEND("VB_TPMU"); + if (0 != tpm_status) { + VBDEBUG(("Can't write FW version to TPM.\n")); + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, + VBNV_RECOVERY_RO_TPM_W_ERROR); + retval = VBERROR_TPM_WRITE_FIRMWARE; + goto VbSelectFirmware_exit; + } + } + + /* Lock firmware versions in TPM */ + VBPERFSTART("VB_TPML"); + tpm_status = RollbackFirmwareLock(); + VBPERFEND("VB_TPML"); + if (0 != tpm_status) { + VBDEBUG(("Unable to lock firmware version in TPM.\n")); + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, + VBNV_RECOVERY_RO_TPM_L_ERROR); + retval = VBERROR_TPM_LOCK_FIRMWARE; + goto VbSelectFirmware_exit; + } + } + + /* + * At this point, we have a good idea of how we are going to + * boot. Update the TPM with this state information. + */ + tpm_status = SetTPMBootModeState(is_dev, is_rec, + shared->fw_keyblock_flags); + if (0 != tpm_status) { + VBDEBUG(("Can't update the TPM with boot mode information.\n")); + if (!is_rec) { + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, + VBNV_RECOVERY_RO_TPM_U_ERROR); + retval = VBERROR_TPM_SET_BOOT_MODE_STATE; + goto VbSelectFirmware_exit; + } + } + + /* Success! */ + retval = VBERROR_SUCCESS; + + VbSelectFirmware_exit: + + /* Save NV storage */ + VbNvTeardown(&vnc); + if (vnc.raw_changed) + VbExNvStorageWrite(vnc.raw); + + /* Stop timer */ + shared->timer_vb_select_firmware_exit = VbExGetTimer(); + + /* Should always have a known error code */ + VbAssert(VBERROR_UNKNOWN != retval); + + return retval; } diff --git a/firmware/lib/vboot_api_init.c b/firmware/lib/vboot_api_init.c index a77b230d..ec33f351 100644 --- a/firmware/lib/vboot_api_init.c +++ b/firmware/lib/vboot_api_init.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. * @@ -13,254 +13,303 @@ #include "vboot_common.h" #include "vboot_nvstorage.h" - -VbError_t VbInit(VbCommonParams* cparams, VbInitParams* iparams) { - VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob; - GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; - VbNvContext vnc; - VbError_t retval = VBERROR_SUCCESS; - uint32_t recovery = VBNV_RECOVERY_NOT_REQUESTED; - int is_s3_resume = 0; - uint32_t s3_debug_boot = 0; - uint32_t require_official_os = 0; - uint32_t tpm_version = 0; - uint32_t tpm_status = 0; - int has_virt_dev_switch = 0; - int is_hw_dev = 0; - int is_virt_dev = 0; - uint32_t disable_dev_request = 0; - uint32_t clear_tpm_owner_request = 0; - int is_dev = 0; - - VBDEBUG(("VbInit() input flags 0x%x\n", iparams->flags)); - - /* Initialize output flags */ - iparams->out_flags = 0; - - /* Set up NV storage */ - VbExNvStorageRead(vnc.raw); - VbNvSetup(&vnc); - - /* Initialize shared data structure */ - if (0 != VbSharedDataInit(shared, cparams->shared_data_size)) { - VBDEBUG(("Shared data init error\n")); - return VBERROR_INIT_SHARED_DATA; - } - - shared->timer_vb_init_enter = VbExGetTimer(); - - /* Copy some boot switch flags */ - /* TODO: in next refactor, just save in/out flags in VbSharedData */ - shared->flags = 0; - if (iparams->flags & VB_INIT_FLAG_REC_BUTTON_PRESSED) - shared->flags |= VBSD_BOOT_REC_SWITCH_ON; - if (iparams->flags & VB_INIT_FLAG_WP_ENABLED) - shared->flags |= VBSD_BOOT_FIRMWARE_WP_ENABLED; - if (iparams->flags & VB_INIT_FLAG_SW_WP_ENABLED) - shared->flags |= VBSD_BOOT_FIRMWARE_SW_WP_ENABLED; - if (iparams->flags & VB_INIT_FLAG_S3_RESUME) - shared->flags |= VBSD_BOOT_S3_RESUME; - if (iparams->flags & VB_INIT_FLAG_RO_NORMAL_SUPPORT) - shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT; - if (iparams->flags & VB_INIT_FLAG_EC_SOFTWARE_SYNC) - shared->flags |= VBSD_EC_SOFTWARE_SYNC; - if (iparams->flags & VB_INIT_FLAG_EC_SLOW_UPDATE) - shared->flags |= VBSD_EC_SLOW_UPDATE; - - is_s3_resume = (iparams->flags & VB_INIT_FLAG_S3_RESUME ? 1 : 0); - - /* Check if the OS is requesting a debug S3 reset */ - VbNvGet(&vnc, VBNV_DEBUG_RESET_MODE, &s3_debug_boot); - if (s3_debug_boot) { - if (is_s3_resume) { - VBDEBUG(("VbInit() requesting S3 debug boot\n")); - iparams->out_flags |= VB_INIT_OUT_S3_DEBUG_BOOT; - is_s3_resume = 0; /* Proceed as if this is a normal boot */ - } - - /* Clear the request even if this is a normal boot, since we don't - * want the NEXT S3 resume to be a debug reset unless the OS - * asserts the request again. */ - VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 0); - } - - /* If this isn't a S3 resume, read the current recovery request, then clear - * it so we don't get stuck in recovery mode. */ - if (!is_s3_resume) { - VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &recovery); - VBDEBUG(("VbInit sees recovery request = %d\n", recovery)); - if (VBNV_RECOVERY_NOT_REQUESTED != recovery) - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_NOT_REQUESTED); - } - - /* If the previous boot failed in the firmware somewhere outside of verified - * boot, and recovery is not requested for our own reasons, request recovery - * mode. This gives the calling firmware a way to request recovery if it - * finds something terribly wrong. */ - if (VBNV_RECOVERY_NOT_REQUESTED == recovery && - iparams->flags & VB_INIT_FLAG_PREVIOUS_BOOT_FAIL) { - recovery = VBNV_RECOVERY_RO_FIRMWARE; - } - - /* If recovery button is pressed, override recovery reason. Note that we - * do this in the S3 resume path also. */ - if (iparams->flags & VB_INIT_FLAG_REC_BUTTON_PRESSED) - recovery = VBNV_RECOVERY_RO_MANUAL; - - /* Copy current recovery reason to shared data. If we fail later on, it - * won't matter, since we'll just reboot. */ - shared->recovery_reason = (uint8_t)recovery; - VBDEBUG(("VbInit now sets shared->recovery_reason = %d\n", recovery)); - - /* If this is a S3 resume, resume the TPM. */ - /* FIXME: I think U-Boot won't ever ask us to do this. Can we remove it? */ - if (is_s3_resume) { - if (TPM_SUCCESS != RollbackS3Resume()) { - /* If we can't resume, just do a full reboot. No need to go to recovery - * mode here, since if the TPM is really broken we'll catch it on the - * next boot. */ - retval = VBERROR_TPM_S3_RESUME; - } - } else { - /* Should we pay attention to the TPM's virtual dev-switch? */ - if (iparams->flags & VB_INIT_FLAG_VIRTUAL_DEV_SWITCH) { - shared->flags |= VBSD_HONOR_VIRT_DEV_SWITCH; - has_virt_dev_switch = 1; - } - /* We always believe the HW dev-switch, since there's one attached to servo - * which may be active even on systems without a physical switch. The EC - * may also implement a fake dev-switch for testing. */ - if (iparams->flags & VB_INIT_FLAG_DEV_SWITCH_ON) - is_hw_dev = 1; - /* We may be asked to clear the virtual dev-switch at boot. */ - VbNvGet(&vnc, VBNV_DISABLE_DEV_REQUEST, &disable_dev_request); - - /* Allow GBB flag to override dev switch */ - if (gbb->flags & GBB_FLAG_FORCE_DEV_SWITCH_ON) - is_hw_dev = 1; - - /* Check if we've been explicitly asked to clear the TPM owner */ - VbNvGet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST, &clear_tpm_owner_request); - - VBPERFSTART("VB_TPMI"); - /* Initialize the TPM. If the developer mode state has changed since the - * last boot, we need to clear TPM ownership. If the TPM space is - * initialized by this call, the virtual dev-switch will be disabled by - * default) */ - tpm_status = RollbackFirmwareSetup(recovery, is_hw_dev, disable_dev_request, - clear_tpm_owner_request, - /* two outputs on success */ - &is_virt_dev, &tpm_version); - VBPERFEND("VB_TPMI"); - if (0 != tpm_status) { - VBDEBUG(("Unable to setup TPM and read firmware version (0x%x)\n", - tpm_status)); - - if (TPM_E_MUST_REBOOT == tpm_status) { - /* TPM wants to reboot into the same mode we're in now */ - VBDEBUG(("TPM requires a reboot.\n")); - if (!recovery) { - /* Not recovery mode. Just reboot (not into recovery). */ - retval = VBERROR_TPM_REBOOT_REQUIRED; - goto VbInit_exit; - } else if (VBNV_RECOVERY_RO_TPM_REBOOT != shared->recovery_reason) { - /* In recovery mode now, and we haven't requested a TPM reboot yet, - * so request one. */ - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_REBOOT); - retval = VBERROR_TPM_REBOOT_REQUIRED; - goto VbInit_exit; - } - } - - if (!recovery) { - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_S_ERROR); - VbNvSet(&vnc, VBNV_RECOVERY_SUBCODE, tpm_status); - retval = VBERROR_TPM_FIRMWARE_SETUP; - goto VbInit_exit; - } - } - - /* TPM setup succeeded. What did we learn? */ - shared->fw_version_tpm_start = tpm_version; - shared->fw_version_tpm = tpm_version; - if (is_hw_dev || (has_virt_dev_switch && is_virt_dev)) { - is_dev = 1; - shared->flags |= VBSD_BOOT_DEV_SWITCH_ON; - } - if (disable_dev_request && !is_virt_dev) - VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 0); - if (clear_tpm_owner_request) { - VbNvSet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST, 0); - VbNvSet(&vnc, VBNV_CLEAR_TPM_OWNER_DONE, 1); - } - } - - /* Allow BIOS to load arbitrary option ROMs? */ - if (gbb->flags & GBB_FLAG_LOAD_OPTION_ROMS) - iparams->out_flags |= VB_INIT_OUT_ENABLE_OPROM; - - /* The factory may need to boot custom OSes whenever the dev-switch is on */ - if (is_dev && (gbb->flags & GBB_FLAG_ENABLE_ALTERNATE_OS)) - iparams->out_flags |= VB_INIT_OUT_ENABLE_ALTERNATE_OS; - - /* Set output flags */ - if (VBNV_RECOVERY_NOT_REQUESTED != recovery) { - /* Requesting recovery mode */ - iparams->out_flags |= (VB_INIT_OUT_ENABLE_RECOVERY | - VB_INIT_OUT_CLEAR_RAM | - VB_INIT_OUT_ENABLE_DISPLAY | - VB_INIT_OUT_ENABLE_USB_STORAGE); - } - else if (is_dev) { - /* Developer switch is on, so need to support dev mode */ - iparams->out_flags |= (VB_INIT_OUT_ENABLE_DEVELOPER | - VB_INIT_OUT_CLEAR_RAM | - VB_INIT_OUT_ENABLE_DISPLAY | - VB_INIT_OUT_ENABLE_USB_STORAGE); - /* ... which may or may not include custom OSes */ - VbNvGet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &require_official_os); - if (!require_official_os) - iparams->out_flags |= VB_INIT_OUT_ENABLE_ALTERNATE_OS; - - /* Dev-mode needs the VGA option ROM to be loaded so it can display the - * scary boot screen. If we don't have it, we need to request it and - * reboot so it can be loaded. */ - if ((iparams->flags & VB_INIT_FLAG_OPROM_MATTERS) && - !(iparams->flags & VB_INIT_FLAG_OPROM_LOADED)) { - VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1); - retval = VBERROR_VGA_OPROM_MISMATCH; - VBDEBUG(("VbInit() needs oprom, doesn't have it\n")); - } - - } else { - /* Normal mode, so disable dev_boot_* flags. This ensures they will be - * initially disabled if the user later transitions back into developer - * mode. */ - VbNvSet(&vnc, VBNV_DEV_BOOT_USB, 0); - VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 0); - VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 0); - - /* If we don't need the VGA option ROM but got it anyway, stop asking for - * it and reboot in case there's some vulnerability in using it. */ - if ((iparams->flags & VB_INIT_FLAG_OPROM_MATTERS) && - (iparams->flags & VB_INIT_FLAG_OPROM_LOADED)) { - VbNvSet(&vnc, VBNV_OPROM_NEEDED, 0); - retval = VBERROR_VGA_OPROM_MISMATCH; - VBDEBUG(("VbInit() has oprom, doesn't need it\n")); - } - } - -VbInit_exit: - - /* Tear down NV storage */ - VbNvTeardown(&vnc); - if (vnc.raw_changed) - VbExNvStorageWrite(vnc.raw); - - VBDEBUG(("VbInit() output flags 0x%x\n", iparams->out_flags)); - - shared->timer_vb_init_exit = VbExGetTimer(); - - VBDEBUG(("VbInit() returning 0x%x\n", retval)); - return retval; +VbError_t VbInit(VbCommonParams *cparams, VbInitParams *iparams) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + GoogleBinaryBlockHeader *gbb = + (GoogleBinaryBlockHeader *)cparams->gbb_data; + VbNvContext vnc; + VbError_t retval = VBERROR_SUCCESS; + uint32_t recovery = VBNV_RECOVERY_NOT_REQUESTED; + int is_s3_resume = 0; + uint32_t s3_debug_boot = 0; + uint32_t require_official_os = 0; + uint32_t tpm_version = 0; + uint32_t tpm_status = 0; + int has_virt_dev_switch = 0; + int is_hw_dev = 0; + int is_virt_dev = 0; + uint32_t disable_dev_request = 0; + uint32_t clear_tpm_owner_request = 0; + int is_dev = 0; + + VBDEBUG(("VbInit() input flags 0x%x\n", iparams->flags)); + + /* Initialize output flags */ + iparams->out_flags = 0; + + /* Set up NV storage */ + VbExNvStorageRead(vnc.raw); + VbNvSetup(&vnc); + + /* Initialize shared data structure */ + if (0 != VbSharedDataInit(shared, cparams->shared_data_size)) { + VBDEBUG(("Shared data init error\n")); + return VBERROR_INIT_SHARED_DATA; + } + + shared->timer_vb_init_enter = VbExGetTimer(); + + /* Copy some boot switch flags */ + /* TODO: in next refactor, just save in/out flags in VbSharedData */ + shared->flags = 0; + if (iparams->flags & VB_INIT_FLAG_REC_BUTTON_PRESSED) + shared->flags |= VBSD_BOOT_REC_SWITCH_ON; + if (iparams->flags & VB_INIT_FLAG_WP_ENABLED) + shared->flags |= VBSD_BOOT_FIRMWARE_WP_ENABLED; + if (iparams->flags & VB_INIT_FLAG_SW_WP_ENABLED) + shared->flags |= VBSD_BOOT_FIRMWARE_SW_WP_ENABLED; + if (iparams->flags & VB_INIT_FLAG_S3_RESUME) + shared->flags |= VBSD_BOOT_S3_RESUME; + if (iparams->flags & VB_INIT_FLAG_RO_NORMAL_SUPPORT) + shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT; + if (iparams->flags & VB_INIT_FLAG_EC_SOFTWARE_SYNC) + shared->flags |= VBSD_EC_SOFTWARE_SYNC; + if (iparams->flags & VB_INIT_FLAG_EC_SLOW_UPDATE) + shared->flags |= VBSD_EC_SLOW_UPDATE; + + is_s3_resume = (iparams->flags & VB_INIT_FLAG_S3_RESUME ? 1 : 0); + + /* Check if the OS is requesting a debug S3 reset */ + VbNvGet(&vnc, VBNV_DEBUG_RESET_MODE, &s3_debug_boot); + if (s3_debug_boot) { + if (is_s3_resume) { + VBDEBUG(("VbInit() requesting S3 debug boot\n")); + iparams->out_flags |= VB_INIT_OUT_S3_DEBUG_BOOT; + is_s3_resume = 0; /* Proceed as if normal boot */ + } + + /* + * Clear the request even if this is a normal boot, since we + * don't want the NEXT S3 resume to be a debug reset unless the + * OS asserts the request again. + */ + VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 0); + } + + /* + * If this isn't a S3 resume, read the current recovery request, then + * clear it so we don't get stuck in recovery mode. + */ + if (!is_s3_resume) { + VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &recovery); + VBDEBUG(("VbInit sees recovery request = %d\n", recovery)); + if (VBNV_RECOVERY_NOT_REQUESTED != recovery) + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, + VBNV_RECOVERY_NOT_REQUESTED); + } + + /* + * If the previous boot failed in the firmware somewhere outside of + * verified boot, and recovery is not requested for our own reasons, + * request recovery mode. This gives the calling firmware a way to + * request recovery if it finds something terribly wrong. + */ + if (VBNV_RECOVERY_NOT_REQUESTED == recovery && + iparams->flags & VB_INIT_FLAG_PREVIOUS_BOOT_FAIL) { + recovery = VBNV_RECOVERY_RO_FIRMWARE; + } + + /* + * If recovery button is pressed, override recovery reason. Note that + * we do this in the S3 resume path also. + */ + if (iparams->flags & VB_INIT_FLAG_REC_BUTTON_PRESSED) + recovery = VBNV_RECOVERY_RO_MANUAL; + + /* + * Copy current recovery reason to shared data. If we fail later on, it + * won't matter, since we'll just reboot. + */ + shared->recovery_reason = (uint8_t)recovery; + VBDEBUG(("VbInit now sets shared->recovery_reason = %d\n", recovery)); + + /* + * If this is a S3 resume, resume the TPM. + * + * FIXME: I think U-Boot won't ever ask us to do this. Can we remove + * it? + */ + if (is_s3_resume) { + if (TPM_SUCCESS != RollbackS3Resume()) { + /* + * If we can't resume, just do a full reboot. No need + * to go to recovery mode here, since if the TPM is + * really broken we'll catch it on the next boot. + */ + retval = VBERROR_TPM_S3_RESUME; + } + } else { + /* Should we pay attention to the TPM's virtual dev-switch? */ + if (iparams->flags & VB_INIT_FLAG_VIRTUAL_DEV_SWITCH) { + shared->flags |= VBSD_HONOR_VIRT_DEV_SWITCH; + has_virt_dev_switch = 1; + } + + /* + * We always believe the HW dev-switch, since there's one + * attached to servo which may be active even on systems + * without a physical switch. The EC may also implement a fake + * dev-switch for testing. + */ + if (iparams->flags & VB_INIT_FLAG_DEV_SWITCH_ON) + is_hw_dev = 1; + + /* We may be asked to clear the virtual dev-switch at boot. */ + VbNvGet(&vnc, VBNV_DISABLE_DEV_REQUEST, &disable_dev_request); + + /* Allow GBB flag to override dev switch */ + if (gbb->flags & GBB_FLAG_FORCE_DEV_SWITCH_ON) + is_hw_dev = 1; + + /* Have we been explicitly asked to clear the TPM owner? */ + VbNvGet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST, + &clear_tpm_owner_request); + + VBPERFSTART("VB_TPMI"); + /* + * Initialize the TPM. If the developer mode state has changed + * since the last boot, we need to clear TPM ownership. If the + * TPM space is initialized by this call, the virtual + * dev-switch will be disabled by default) + */ + tpm_status = RollbackFirmwareSetup(recovery, is_hw_dev, + disable_dev_request, + clear_tpm_owner_request, + /* two outputs on success */ + &is_virt_dev, &tpm_version); + VBPERFEND("VB_TPMI"); + + if (0 != tpm_status) { + VBDEBUG(("Unable to setup TPM and read " + "firmware version (0x%x)\n", tpm_status)); + + if (TPM_E_MUST_REBOOT == tpm_status) { + /* + * TPM wants to reboot into the same mode we're + * in now + */ + VBDEBUG(("TPM requires a reboot.\n")); + if (!recovery) { + /* + * Not recovery mode. Just reboot (not + * into recovery). + */ + retval = VBERROR_TPM_REBOOT_REQUIRED; + goto VbInit_exit; + } else if (VBNV_RECOVERY_RO_TPM_REBOOT != + shared->recovery_reason) { + /* + * In recovery mode now, and we haven't + * requested a TPM reboot yet, so + * request one. + */ + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, + VBNV_RECOVERY_RO_TPM_REBOOT); + retval = VBERROR_TPM_REBOOT_REQUIRED; + goto VbInit_exit; + } + } + + if (!recovery) { + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, + VBNV_RECOVERY_RO_TPM_S_ERROR); + VbNvSet(&vnc, VBNV_RECOVERY_SUBCODE, + tpm_status); + retval = VBERROR_TPM_FIRMWARE_SETUP; + goto VbInit_exit; + } + } + + /* TPM setup succeeded. What did we learn? */ + shared->fw_version_tpm_start = tpm_version; + shared->fw_version_tpm = tpm_version; + if (is_hw_dev || (has_virt_dev_switch && is_virt_dev)) { + is_dev = 1; + shared->flags |= VBSD_BOOT_DEV_SWITCH_ON; + } + if (disable_dev_request && !is_virt_dev) + VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 0); + if (clear_tpm_owner_request) { + VbNvSet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST, 0); + VbNvSet(&vnc, VBNV_CLEAR_TPM_OWNER_DONE, 1); + } + } + + /* Allow BIOS to load arbitrary option ROMs? */ + if (gbb->flags & GBB_FLAG_LOAD_OPTION_ROMS) + iparams->out_flags |= VB_INIT_OUT_ENABLE_OPROM; + + /* Factory may need to boot custom OSes when the dev-switch is on */ + if (is_dev && (gbb->flags & GBB_FLAG_ENABLE_ALTERNATE_OS)) + iparams->out_flags |= VB_INIT_OUT_ENABLE_ALTERNATE_OS; + + /* Set output flags */ + if (VBNV_RECOVERY_NOT_REQUESTED != recovery) { + /* Requesting recovery mode */ + iparams->out_flags |= (VB_INIT_OUT_ENABLE_RECOVERY | + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE); + } else if (is_dev) { + /* Developer switch is on, so need to support dev mode */ + iparams->out_flags |= (VB_INIT_OUT_ENABLE_DEVELOPER | + VB_INIT_OUT_CLEAR_RAM | + VB_INIT_OUT_ENABLE_DISPLAY | + VB_INIT_OUT_ENABLE_USB_STORAGE); + /* ... which may or may not include custom OSes */ + VbNvGet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &require_official_os); + if (!require_official_os) + iparams->out_flags |= VB_INIT_OUT_ENABLE_ALTERNATE_OS; + + /* + * Dev-mode needs the VGA option ROM to be loaded so it can + * display the scary boot screen. If we don't have it, we need + * to request it and reboot so it can be loaded. + */ + if ((iparams->flags & VB_INIT_FLAG_OPROM_MATTERS) && + !(iparams->flags & VB_INIT_FLAG_OPROM_LOADED)) { + VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1); + retval = VBERROR_VGA_OPROM_MISMATCH; + VBDEBUG(("VbInit() needs oprom, doesn't have it\n")); + } + + } else { + /* + * Normal mode, so disable dev_boot_* flags. This ensures they + * will be initially disabled if the user later transitions + * back into developer mode. + */ + VbNvSet(&vnc, VBNV_DEV_BOOT_USB, 0); + VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 0); + VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 0); + + /* + * If we don't need the VGA option ROM but got it anyway, stop + * asking for it and reboot in case there's some vulnerability + * in using it. + */ + if ((iparams->flags & VB_INIT_FLAG_OPROM_MATTERS) && + (iparams->flags & VB_INIT_FLAG_OPROM_LOADED)) { + VbNvSet(&vnc, VBNV_OPROM_NEEDED, 0); + retval = VBERROR_VGA_OPROM_MISMATCH; + VBDEBUG(("VbInit() has oprom, doesn't need it\n")); + } + } + + VbInit_exit: + + /* Tear down NV storage */ + VbNvTeardown(&vnc); + if (vnc.raw_changed) + VbExNvStorageWrite(vnc.raw); + + VBDEBUG(("VbInit() output flags 0x%x\n", iparams->out_flags)); + + shared->timer_vb_init_exit = VbExGetTimer(); + + VBDEBUG(("VbInit() returning 0x%x\n", retval)); + + return retval; } diff --git a/firmware/lib/vboot_api_kernel.c b/firmware/lib/vboot_api_kernel.c index 70895ff7..a55d8064 100644 --- a/firmware/lib/vboot_api_kernel.c +++ b/firmware/lib/vboot_api_kernel.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. * @@ -15,764 +15,999 @@ #include "vboot_display.h" #include "vboot_nvstorage.h" - /* Global variables */ static VbNvContext vnc; - #ifdef CHROMEOS_ENVIRONMENT -/* Global variable accessors for unit tests */ -VbNvContext* VbApiKernelGetVnc(void) { - return &vnc; +/* Global variable accessor for unit tests */ + +VbNvContext *VbApiKernelGetVnc(void) +{ + return &vnc; } #endif - -/* Set recovery request */ -static void VbSetRecoveryRequest(uint32_t recovery_request) { - VBDEBUG(("VbSetRecoveryRequest(%d)\n", (int)recovery_request)); - VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, recovery_request); +/** + * Set recovery request (called from vboot_api_kernel.c functions only) + */ +static void VbSetRecoveryRequest(uint32_t recovery_request) +{ + VBDEBUG(("VbSetRecoveryRequest(%d)\n", (int)recovery_request)); + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, recovery_request); } - -/* Attempt loading a kernel from the specified type(s) of disks. If - * successful, sets p->disk_handle to the disk for the kernel and returns +/** + * Attempt loading a kernel from the specified type(s) of disks. + * + * If successful, sets p->disk_handle to the disk for the kernel and returns * VBERROR_SUCCESS. * * Returns VBERROR_NO_DISK_FOUND if no disks of the specified type were found. * - * May return other VBERROR_ codes for other failures. */ -uint32_t VbTryLoadKernel(VbCommonParams* cparams, LoadKernelParams* p, - uint32_t get_info_flags) { - VbError_t retval = VBERROR_UNKNOWN; - VbDiskInfo* disk_info = NULL; - uint32_t disk_count = 0; - uint32_t i; - - VBDEBUG(("VbTryLoadKernel() start, get_info_flags=0x%x\n", - (unsigned)get_info_flags)); - - p->disk_handle = NULL; - - /* Find disks */ - if (VBERROR_SUCCESS != VbExDiskGetInfo(&disk_info, &disk_count, - get_info_flags)) - disk_count = 0; - - VBDEBUG(("VbTryLoadKernel() found %d disks\n", (int)disk_count)); - if (0 == disk_count) { - VbSetRecoveryRequest(VBNV_RECOVERY_RW_NO_DISK); - return VBERROR_NO_DISK_FOUND; - } - - /* Loop over disks */ - for (i = 0; i < disk_count; i++) { - VBDEBUG(("VbTryLoadKernel() trying disk %d\n", (int)i)); - /* Sanity-check what we can. FWIW, VbTryLoadKernel() is always called - * with only a single bit set in get_info_flags - */ - if (512 != disk_info[i].bytes_per_lba || /* cgptlib restriction */ - 32 > disk_info[i].lba_count || /* ditto */ - get_info_flags != disk_info[i].flags) { /* got only what we asked for */ - VBDEBUG((" skipping: bytes_per_lba=%lld lba_count=%lld flags=0x%x\n", - disk_info[i].bytes_per_lba, disk_info[i].lba_count, - disk_info[i].flags)); - continue; - } - p->disk_handle = disk_info[i].handle; - p->bytes_per_lba = disk_info[i].bytes_per_lba; - p->ending_lba = disk_info[i].lba_count - 1; - retval = LoadKernel(p); - VBDEBUG(("VbTryLoadKernel() LoadKernel() returned %d\n", retval)); - - /* Stop now if we found a kernel */ - /* TODO: If recovery requested, should track the farthest we get, instead - * of just returning the value from the last disk attempted. */ - if (VBERROR_SUCCESS == retval) - break; - } - - /* If we didn't find any good kernels, don't return a disk handle. */ - if (VBERROR_SUCCESS != retval) { - VbSetRecoveryRequest(VBNV_RECOVERY_RW_NO_KERNEL); - p->disk_handle = NULL; - } - - VbExDiskFreeInfo(disk_info, p->disk_handle); - - /* Pass through return code. Recovery reason (if any) has already been set - * by LoadKernel(). */ - return retval; + * May return other VBERROR_ codes for other failures. + */ +uint32_t VbTryLoadKernel(VbCommonParams *cparams, LoadKernelParams *p, + uint32_t get_info_flags) +{ + VbError_t retval = VBERROR_UNKNOWN; + VbDiskInfo* disk_info = NULL; + uint32_t disk_count = 0; + uint32_t i; + + VBDEBUG(("VbTryLoadKernel() start, get_info_flags=0x%x\n", + (unsigned)get_info_flags)); + + p->disk_handle = NULL; + + /* Find disks */ + if (VBERROR_SUCCESS != VbExDiskGetInfo(&disk_info, &disk_count, + get_info_flags)) + disk_count = 0; + + VBDEBUG(("VbTryLoadKernel() found %d disks\n", (int)disk_count)); + if (0 == disk_count) { + VbSetRecoveryRequest(VBNV_RECOVERY_RW_NO_DISK); + return VBERROR_NO_DISK_FOUND; + } + + /* Loop over disks */ + for (i = 0; i < disk_count; i++) { + VBDEBUG(("VbTryLoadKernel() trying disk %d\n", (int)i)); + /* + * Sanity-check what we can. FWIW, VbTryLoadKernel() is always + * called with only a single bit set in get_info_flags. + * + * Ensure 512-byte sectors and non-trivially sized disk (for + * cgptlib) and that we got a partition with only the flags we + * asked for. + */ + if (512 != disk_info[i].bytes_per_lba || + 32 > disk_info[i].lba_count || + get_info_flags != disk_info[i].flags) { + VBDEBUG((" skipping: bytes_per_lba=%lld " + "lba_count=%lld flags=0x%x\n", + disk_info[i].bytes_per_lba, + disk_info[i].lba_count, + disk_info[i].flags)); + continue; + } + p->disk_handle = disk_info[i].handle; + p->bytes_per_lba = disk_info[i].bytes_per_lba; + p->ending_lba = disk_info[i].lba_count - 1; + retval = LoadKernel(p); + VBDEBUG(("VbTryLoadKernel() LoadKernel() = %d\n", retval)); + + /* + * Stop now if we found a kernel. + * + * TODO: If recovery requested, should track the farthest we + * get, instead of just returning the value from the last disk + * attempted. + */ + if (VBERROR_SUCCESS == retval) + break; + } + + /* If we didn't find any good kernels, don't return a disk handle. */ + if (VBERROR_SUCCESS != retval) { + VbSetRecoveryRequest(VBNV_RECOVERY_RW_NO_KERNEL); + p->disk_handle = NULL; + } + + VbExDiskFreeInfo(disk_info, p->disk_handle); + + /* + * Pass through return code. Recovery reason (if any) has already been + * set by LoadKernel(). + */ + return retval; } #define CONFIRM_KEY_DELAY 20 /* Check confirm screen keys every 20ms */ -/* Ask the user to confirm something. We should display whatever the question - * is first, then call this. ESC is always "no", ENTER is always "yes", and - * we'll specify what SPACE means. We don't return until one of those keys is - * pressed, or until asked to shut down. - * - * Returns: 1=yes, 0=no, -1 = shutdown. - */ -static int VbUserConfirms(VbCommonParams* cparams, int space_means_no) { - uint32_t key; - - VBDEBUG(("Entering %s(%d)\n", __func__, space_means_no)); - - /* Await further instructions */ - while (1) { - if (VbExIsShutdownRequested()) - return -1; - key = VbExKeyboardRead(); - switch (key) { - case '\r': - VBDEBUG(("%s() - Yes (1)\n", __func__)); - return 1; - break; - case ' ': - VBDEBUG(("%s() - Space (%s)\n", __func__, space_means_no)); - if (space_means_no) - return 0; - break; - case 0x1b: - VBDEBUG(("%s() - No (0)\n", __func__)); - return 0; - break; - default: - VbCheckDisplayKey(cparams, key, &vnc); - } - VbExSleepMs(CONFIRM_KEY_DELAY); - } - /* not reached, but compiler will complain without it */ - return -1; +int VbUserConfirms(VbCommonParams *cparams, int space_means_no) +{ + uint32_t key; + + VBDEBUG(("Entering %s(%d)\n", __func__, space_means_no)); + + /* Await further instructions */ + while (1) { + if (VbExIsShutdownRequested()) + return -1; + key = VbExKeyboardRead(); + switch (key) { + case '\r': + VBDEBUG(("%s() - Yes (1)\n", __func__)); + return 1; + break; + case ' ': + VBDEBUG(("%s() - Space (%s)\n", __func__, + space_means_no)); + if (space_means_no) + return 0; + break; + case 0x1b: + VBDEBUG(("%s() - No (0)\n", __func__)); + return 0; + break; + default: + VbCheckDisplayKey(cparams, key, &vnc); + } + VbExSleepMs(CONFIRM_KEY_DELAY); + } + + /* Not reached, but compiler will complain without it */ + return -1; } -/* Handle a normal boot. */ -VbError_t VbBootNormal(VbCommonParams* cparams, LoadKernelParams* p) { - /* Boot from fixed disk only */ - VBDEBUG(("Entering %s()\n", __func__)); - return VbTryLoadKernel(cparams, p, VB_DISK_FLAG_FIXED); +VbError_t VbBootNormal(VbCommonParams *cparams, LoadKernelParams *p) +{ + /* Boot from fixed disk only */ + VBDEBUG(("Entering %s()\n", __func__)); + return VbTryLoadKernel(cparams, p, VB_DISK_FLAG_FIXED); } -/* Handle a developer-mode boot */ -VbError_t VbBootDeveloper(VbCommonParams* cparams, LoadKernelParams* p) { - GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; - VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob; - uint32_t allow_usb = 0, allow_legacy = 0; - VbAudioContext* audio = 0; - - VBDEBUG(("Entering %s()\n", __func__)); - - /* Check if USB booting is allowed */ - VbNvGet(&vnc, VBNV_DEV_BOOT_USB, &allow_usb); - VbNvGet(&vnc, VBNV_DEV_BOOT_LEGACY, &allow_legacy); - /* Handle GBB flag override */ - if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_USB) - allow_usb = 1; - if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_LEGACY) - allow_legacy = 1; - - /* Show the dev mode warning screen */ - VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc); - - /* Get audio/delay context */ - audio = VbAudioOpen(cparams); - - /* We'll loop until we finish the delay or are interrupted */ - do { - uint32_t key; - - if (VbExIsShutdownRequested()) { - VBDEBUG(("VbBootDeveloper() - shutdown is requested!\n")); - VbAudioClose(audio); - return VBERROR_SHUTDOWN_REQUESTED; - } - - key = VbExKeyboardRead(); - switch (key) { - case 0: - /* nothing pressed */ - break; - case '\r': - /* Enter only disables the virtual dev switch if allowed by GBB */ - if (!(gbb->flags & GBB_FLAG_ENTER_TRIGGERS_TONORM)) - break; - case ' ': - /* See if we should disable the virtual dev-mode switch. */ - VBDEBUG(("%s shared->flags=0x%x\n", __func__, shared->flags)); - if (shared->flags & VBSD_HONOR_VIRT_DEV_SWITCH && - shared->flags & VBSD_BOOT_DEV_SWITCH_ON) { - VbAudioClose(audio); /* Stop the countdown while we go ask... */ - if (gbb->flags & GBB_FLAG_FORCE_DEV_SWITCH_ON) { - /* TONORM won't work (only for non-shipping devices). */ - VBDEBUG(("%s() - TONORM rejected by GBB_FLAG_FORCE_DEV_SWITCH_ON\n", - __func__)); - VbExDisplayDebugInfo("WARNING: TONORM is prohibited by " - "GBB_FLAG_FORCE_DEV_SWITCH_ON.\n\n"); - VbExBeep(120, 400); - break; - } - VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_TO_NORM, 0, &vnc); - switch (VbUserConfirms(cparams, 0)) { /* Ignore space */ - case 1: - VBDEBUG(("%s() - leaving dev-mode...\n", __func__)); - VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 1); - VbDisplayScreen(cparams, VB_SCREEN_TO_NORM_CONFIRMED, 0, &vnc); - VbExSleepMs(5000); - return VBERROR_TPM_REBOOT_REQUIRED; - case -1: - VBDEBUG(("%s() - shutdown requested\n", __func__)); - return VBERROR_SHUTDOWN_REQUESTED; - default: /* stay in dev-mode */ - VBDEBUG(("%s() - stay in dev-mode\n", __func__)); - VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc); - audio = VbAudioOpen(cparams); /* Start new countdown */ - } - } else { - /* No virtual dev-mode switch, so go directly to recovery mode */ - VBDEBUG(("%s() - going to recovery\n", __func__)); - VbSetRecoveryRequest(VBNV_RECOVERY_RW_DEV_SCREEN); - VbAudioClose(audio); - return VBERROR_LOAD_KERNEL_RECOVERY; - } - break; - case 0x04: - /* Ctrl+D = dismiss warning; advance to timeout */ - VBDEBUG(("VbBootDeveloper() - user pressed Ctrl+D; skip delay\n")); - goto fallout; - break; - case 0x0c: - VBDEBUG(("VbBootDeveloper() - user pressed Ctrl+L; Try legacy boot\n")); - /* If VbExLegacy() succeeds, it will never return. - * If it returns, beep. - */ - if (allow_legacy) - VbExLegacy(); - else - VBDEBUG(("VbBootDeveloper() - Legacy boot is disabled\n")); - - VbExBeep(120, 400); - VbExSleepMs(120); - VbExBeep(120, 400); - break; - /* The Ctrl-Enter is special for Lumpy test purpose. */ - case VB_KEY_CTRL_ENTER: - case 0x15: - /* Ctrl+U = try USB boot, or beep if failure */ - VBDEBUG(("VbBootDeveloper() - user pressed Ctrl+U; try USB\n")); - if (!allow_usb) { - VBDEBUG(("VbBootDeveloper() - USB booting is disabled\n")); - VbExDisplayDebugInfo("WARNING: Booting from external media (USB/SD) " - "has not been enabled. Refer to the " - "developer-mode documentation for details.\n"); - VbExBeep(120, 400); - VbExSleepMs(120); - VbExBeep(120, 400); - } else { - /* Clear the screen to show we get the Ctrl+U key press. */ - VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, &vnc); - if (VBERROR_SUCCESS == - VbTryLoadKernel(cparams, p, VB_DISK_FLAG_REMOVABLE)) { - VBDEBUG(("VbBootDeveloper() - booting USB\n")); - VbAudioClose(audio); - return VBERROR_SUCCESS; - } else { - VBDEBUG(("VbBootDeveloper() - no kernel found on USB\n")); - VbExBeep(250, 200); - VbExSleepMs(120); - /* Clear recovery requests from failed kernel loading, so - * that powering off at this point doesn't put us into - * recovery mode. */ - VbSetRecoveryRequest(VBNV_RECOVERY_NOT_REQUESTED); - /* Show the dev mode warning screen again */ - VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc); - } - } - break; - default: - VBDEBUG(("VbBootDeveloper() - pressed key %d\n", key)); - VbCheckDisplayKey(cparams, key, &vnc); - break; - } - - } while( VbAudioLooping(audio) ); - -fallout: - /* Timeout or Ctrl+D; attempt loading from fixed disk */ - VBDEBUG(("VbBootDeveloper() - trying fixed disk\n")); - VbAudioClose(audio); - return VbTryLoadKernel(cparams, p, VB_DISK_FLAG_FIXED); +VbError_t VbBootDeveloper(VbCommonParams *cparams, LoadKernelParams *p) +{ + GoogleBinaryBlockHeader *gbb = + (GoogleBinaryBlockHeader *)cparams->gbb_data; + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + uint32_t allow_usb = 0, allow_legacy = 0; + VbAudioContext *audio = 0; + + VBDEBUG(("Entering %s()\n", __func__)); + + /* Check if USB booting is allowed */ + VbNvGet(&vnc, VBNV_DEV_BOOT_USB, &allow_usb); + VbNvGet(&vnc, VBNV_DEV_BOOT_LEGACY, &allow_legacy); + + /* Handle GBB flag override */ + if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_USB) + allow_usb = 1; + if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_LEGACY) + allow_legacy = 1; + + /* Show the dev mode warning screen */ + VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc); + + /* Get audio/delay context */ + audio = VbAudioOpen(cparams); + + /* We'll loop until we finish the delay or are interrupted */ + do { + uint32_t key; + + if (VbExIsShutdownRequested()) { + VBDEBUG(("VbBootDeveloper() - shutdown requested!\n")); + VbAudioClose(audio); + return VBERROR_SHUTDOWN_REQUESTED; + } + + key = VbExKeyboardRead(); + switch (key) { + case 0: + /* nothing pressed */ + break; + case '\r': + /* Only disable virtual dev switch if allowed by GBB */ + if (!(gbb->flags & GBB_FLAG_ENTER_TRIGGERS_TONORM)) + break; + case ' ': + /* See if we should disable virtual dev-mode switch. */ + VBDEBUG(("%s shared->flags=0x%x\n", + __func__, shared->flags)); + if (shared->flags & VBSD_HONOR_VIRT_DEV_SWITCH && + shared->flags & VBSD_BOOT_DEV_SWITCH_ON) { + /* Stop the countdown while we go ask... */ + VbAudioClose(audio); + if (gbb->flags & GBB_FLAG_FORCE_DEV_SWITCH_ON) { + /* + * TONORM won't work (only for + * non-shipping devices). + */ + VBDEBUG(("%s() - TONORM rejected by " + "FORCE_DEV_SWITCH_ON\n", + __func__)); + VbExDisplayDebugInfo( + "WARNING: TONORM prohibited by " + "GBB FORCE_DEV_SWITCH_ON.\n\n"); + VbExBeep(120, 400); + break; + } + VbDisplayScreen(cparams, + VB_SCREEN_DEVELOPER_TO_NORM, + 0, &vnc); + /* Ignore space in VbUserConfirms()... */ + switch (VbUserConfirms(cparams, 0)) { + case 1: + VBDEBUG(("%s() - leaving dev-mode.\n", + __func__)); + VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, + 1); + VbDisplayScreen( + cparams, + VB_SCREEN_TO_NORM_CONFIRMED, + 0, &vnc); + VbExSleepMs(5000); + return VBERROR_TPM_REBOOT_REQUIRED; + case -1: + VBDEBUG(("%s() - shutdown requested\n", + __func__)); + return VBERROR_SHUTDOWN_REQUESTED; + default: + /* Stay in dev-mode */ + VBDEBUG(("%s() - stay in dev-mode\n", + __func__)); + VbDisplayScreen( + cparams, + VB_SCREEN_DEVELOPER_WARNING, + 0, &vnc); + /* Start new countdown */ + audio = VbAudioOpen(cparams); + } + } else { + /* + * No virtual dev-mode switch, so go directly + * to recovery mode. + */ + VBDEBUG(("%s() - going to recovery\n", + __func__)); + VbSetRecoveryRequest( + VBNV_RECOVERY_RW_DEV_SCREEN); + VbAudioClose(audio); + return VBERROR_LOAD_KERNEL_RECOVERY; + } + break; + case 0x04: + /* Ctrl+D = dismiss warning; advance to timeout */ + VBDEBUG(("VbBootDeveloper() - " + "user pressed Ctrl+D; skip delay\n")); + goto fallout; + break; + case 0x0c: + VBDEBUG(("VbBootDeveloper() - " + "user pressed Ctrl+L; Try legacy boot\n")); + /* + * If VbExLegacy() succeeds, it will never return. If + * it returns, beep. + */ + if (allow_legacy) + VbExLegacy(); + else + VBDEBUG(("VbBootDeveloper() - " + "Legacy boot is disabled\n")); + + VbExBeep(120, 400); + VbExSleepMs(120); + VbExBeep(120, 400); + break; + + case VB_KEY_CTRL_ENTER: + /* + * The Ctrl-Enter is special for Lumpy test purpose; + * fall through to Ctrl+U handler. + */ + case 0x15: + /* Ctrl+U = try USB boot, or beep if failure */ + VBDEBUG(("VbBootDeveloper() - " + "user pressed Ctrl+U; try USB\n")); + if (!allow_usb) { + VBDEBUG(("VbBootDeveloper() - " + "USB booting is disabled\n")); + VbExDisplayDebugInfo( + "WARNING: Booting from external media " + "(USB/SD) has not been enabled. Refer " + "to the developer-mode documentation " + "for details.\n"); + VbExBeep(120, 400); + VbExSleepMs(120); + VbExBeep(120, 400); + } else { + /* + * Clear the screen to show we get the Ctrl+U + * key press. + */ + VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, + &vnc); + if (VBERROR_SUCCESS == + VbTryLoadKernel(cparams, p, + VB_DISK_FLAG_REMOVABLE)) { + VBDEBUG(("VbBootDeveloper() - " + "booting USB\n")); + VbAudioClose(audio); + return VBERROR_SUCCESS; + } else { + VBDEBUG(("VbBootDeveloper() - " + "no kernel found on USB\n")); + VbExBeep(250, 200); + VbExSleepMs(120); + /* + * Clear recovery requests from failed + * kernel loading, so that powering off + * at this point doesn't put us into + * recovery mode. + */ + VbSetRecoveryRequest( + VBNV_RECOVERY_NOT_REQUESTED); + /* Show dev mode warning screen again */ + VbDisplayScreen( + cparams, + VB_SCREEN_DEVELOPER_WARNING, + 0, &vnc); + } + } + break; + default: + VBDEBUG(("VbBootDeveloper() - pressed key %d\n", key)); + VbCheckDisplayKey(cparams, key, &vnc); + break; + } + } while(VbAudioLooping(audio)); + + fallout: + /* Timeout or Ctrl+D; attempt loading from fixed disk */ + VBDEBUG(("VbBootDeveloper() - trying fixed disk\n")); + VbAudioClose(audio); + return VbTryLoadKernel(cparams, p, VB_DISK_FLAG_FIXED); } /* Delay in recovery mode */ #define REC_DISK_DELAY 1000 /* Check disks every 1s */ #define REC_KEY_DELAY 20 /* Check keys every 20ms */ -/* Handle a recovery-mode boot */ -VbError_t VbBootRecovery(VbCommonParams* cparams, LoadKernelParams* p) { - VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob; - uint32_t retval; - uint32_t key; - int i; - - VBDEBUG(("VbBootRecovery() start\n")); - - /* If the dev-mode switch is off and the user didn't press the recovery - * button, require removal of all external media. */ - if (!(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) && - !(shared->flags & VBSD_BOOT_REC_SWITCH_ON)) { - VbDiskInfo* disk_info = NULL; - uint32_t disk_count = 0; - - VBDEBUG(("VbBootRecovery() forcing device removal\n")); - - while (1) { - if (VBERROR_SUCCESS != VbExDiskGetInfo(&disk_info, &disk_count, - VB_DISK_FLAG_REMOVABLE)) - disk_count = 0; - VbExDiskFreeInfo(disk_info, NULL); - - if (0 == disk_count) { - VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, &vnc); - break; - } - - VBDEBUG(("VbBootRecovery() waiting for %d disks to be removed\n", - (int)disk_count)); - - VbDisplayScreen(cparams, VB_SCREEN_RECOVERY_REMOVE, 0, &vnc); - - /* Scan keyboard more frequently than media, since x86 platforms - * don't like to scan USB too rapidly. */ - for (i = 0; i < REC_DISK_DELAY; i += REC_KEY_DELAY) { - VbCheckDisplayKey(cparams, VbExKeyboardRead(), &vnc); - if (VbExIsShutdownRequested()) - return VBERROR_SHUTDOWN_REQUESTED; - VbExSleepMs(REC_KEY_DELAY); - } - } - } - - /* Loop and wait for a recovery image */ - while (1) { - VBDEBUG(("VbBootRecovery() attempting to load kernel2\n")); - retval = VbTryLoadKernel(cparams, p, VB_DISK_FLAG_REMOVABLE); - - /* Clear recovery requests from failed kernel loading, since we're - * already in recovery mode. Do this now, so that powering off after - * inserting an invalid disk doesn't leave us stuck in recovery mode. */ - VbSetRecoveryRequest(VBNV_RECOVERY_NOT_REQUESTED); - - if (VBERROR_SUCCESS == retval) - break; /* Found a recovery kernel */ - - VbDisplayScreen(cparams, VBERROR_NO_DISK_FOUND == retval ? - VB_SCREEN_RECOVERY_INSERT : VB_SCREEN_RECOVERY_NO_GOOD, - 0, &vnc); - - /* Scan keyboard more frequently than media, since x86 platforms don't like - * to scan USB too rapidly. */ - for (i = 0; i < REC_DISK_DELAY; i += REC_KEY_DELAY) { - key = VbExKeyboardRead(); - /* We might want to enter dev-mode from the Insert screen if... */ - if (key == 0x04 && /* user pressed Ctrl-D */ - shared->flags & VBSD_HONOR_VIRT_DEV_SWITCH && /* we can do that */ - !(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) && /* not in dev-mode */ - (shared->flags & VBSD_BOOT_REC_SWITCH_ON) && /* user forced rec */ - VbExTrustEC()) { /* EC isn't pwned */ - /* Ask the user to confirm entering dev-mode */ - VbDisplayScreen(cparams, VB_SCREEN_RECOVERY_TO_DEV, 0, &vnc); - switch (VbUserConfirms(cparams, 1)) { /* SPACE means no */ - case 1: - VBDEBUG(("%s() - Enabling dev-mode...\n", __func__)); - if (TPM_SUCCESS != SetVirtualDevMode(1)) - return VBERROR_TPM_SET_BOOT_MODE_STATE; - VBDEBUG(("%s() - Reboot so it will take effect\n", __func__)); - return VBERROR_TPM_REBOOT_REQUIRED; - case -1: - VBDEBUG(("%s() - Shutdown requested\n", __func__)); - return VBERROR_SHUTDOWN_REQUESTED; - default: /* zero, actually */ - VBDEBUG(("%s() - Not enabling dev-mode\n", __func__)); - /* Jump out of the outer loop to refresh the display quickly. */ - i = 4; - break; - } - } else - VbCheckDisplayKey(cparams, key, &vnc); - if (VbExIsShutdownRequested()) - return VBERROR_SHUTDOWN_REQUESTED; - VbExSleepMs(REC_KEY_DELAY); - } - } - - return VBERROR_SUCCESS; +VbError_t VbBootRecovery(VbCommonParams *cparams, LoadKernelParams *p) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + uint32_t retval; + uint32_t key; + int i; + + VBDEBUG(("VbBootRecovery() start\n")); + + /* + * If the dev-mode switch is off and the user didn't press the recovery + * button, require removal of all external media. + */ + if (!(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) && + !(shared->flags & VBSD_BOOT_REC_SWITCH_ON)) { + VbDiskInfo *disk_info = NULL; + uint32_t disk_count = 0; + + VBDEBUG(("VbBootRecovery() forcing device removal\n")); + + while (1) { + if (VBERROR_SUCCESS != + VbExDiskGetInfo(&disk_info, &disk_count, + VB_DISK_FLAG_REMOVABLE)) + disk_count = 0; + + VbExDiskFreeInfo(disk_info, NULL); + + if (0 == disk_count) { + VbDisplayScreen(cparams, VB_SCREEN_BLANK, + 0, &vnc); + break; + } + + VBDEBUG(("VbBootRecovery() " + "waiting for %d disks to be removed\n", + (int)disk_count)); + + VbDisplayScreen(cparams, VB_SCREEN_RECOVERY_REMOVE, + 0, &vnc); + + /* + * Scan keyboard more frequently than media, since x86 + * platforms don't like to scan USB too rapidly. + */ + for (i = 0; i < REC_DISK_DELAY; i += REC_KEY_DELAY) { + VbCheckDisplayKey(cparams, VbExKeyboardRead(), + &vnc); + if (VbExIsShutdownRequested()) + return VBERROR_SHUTDOWN_REQUESTED; + VbExSleepMs(REC_KEY_DELAY); + } + } + } + + /* Loop and wait for a recovery image */ + while (1) { + VBDEBUG(("VbBootRecovery() attempting to load kernel2\n")); + retval = VbTryLoadKernel(cparams, p, VB_DISK_FLAG_REMOVABLE); + + /* + * Clear recovery requests from failed kernel loading, since + * we're already in recovery mode. Do this now, so that + * powering off after inserting an invalid disk doesn't leave + * us stuck in recovery mode. + */ + VbSetRecoveryRequest(VBNV_RECOVERY_NOT_REQUESTED); + + if (VBERROR_SUCCESS == retval) + break; /* Found a recovery kernel */ + + VbDisplayScreen(cparams, VBERROR_NO_DISK_FOUND == retval ? + VB_SCREEN_RECOVERY_INSERT : + VB_SCREEN_RECOVERY_NO_GOOD, + 0, &vnc); + + /* + * Scan keyboard more frequently than media, since x86 + * platforms don't like to scan USB too rapidly. + */ + for (i = 0; i < REC_DISK_DELAY; i += REC_KEY_DELAY) { + key = VbExKeyboardRead(); + /* + * We might want to enter dev-mode from the Insert + * screen if all of the following are true: + * - user pressed Ctrl-D + * - we can honor the virtual dev switch + * - not already in dev mode + * - user forced recovery mode + * - EC isn't pwned + */ + if (key == 0x04 && + shared->flags & VBSD_HONOR_VIRT_DEV_SWITCH && + !(shared->flags & VBSD_BOOT_DEV_SWITCH_ON) && + (shared->flags & VBSD_BOOT_REC_SWITCH_ON) && + VbExTrustEC()) { + /* Ask the user to confirm entering dev-mode */ + VbDisplayScreen(cparams, + VB_SCREEN_RECOVERY_TO_DEV, + 0, &vnc); + /* SPACE means no... */ + switch (VbUserConfirms(cparams, 1)) { + case 1: + VBDEBUG(("%s() Enabling dev-mode...\n", + __func__)); + if (TPM_SUCCESS != SetVirtualDevMode(1)) + return VBERROR_TPM_SET_BOOT_MODE_STATE; + VBDEBUG(("%s() Reboot so it will take " + "effect\n", __func__)); + return VBERROR_TPM_REBOOT_REQUIRED; + case -1: + VBDEBUG(("%s() - Shutdown requested\n", + __func__)); + return VBERROR_SHUTDOWN_REQUESTED; + default: /* zero, actually */ + VBDEBUG(("%s() - Not enabling " + "dev-mode\n", __func__)); + /* + * Jump out of the outer loop to + * refresh the display quickly. + */ + i = 4; + break; + } + } else { + VbCheckDisplayKey(cparams, key, &vnc); + } + if (VbExIsShutdownRequested()) + return VBERROR_SHUTDOWN_REQUESTED; + VbExSleepMs(REC_KEY_DELAY); + } + } + + return VBERROR_SUCCESS; } -/* Wrapper around VbExEcProtectRW() which sets recovery reason on error */ -static VbError_t EcProtectRW(void) { - int rv = VbExEcProtectRW(); - - if (rv == VBERROR_EC_REBOOT_TO_RO_REQUIRED) { - VBDEBUG(("VbExEcProtectRW() needs reboot\n")); - } else if (rv != VBERROR_SUCCESS) { - VBDEBUG(("VbExEcProtectRW() returned %d\n", rv)); - VbSetRecoveryRequest(VBNV_RECOVERY_EC_PROTECT); - } - return rv; +/** + * Wrapper around VbExEcProtectRW() which sets recovery reason on error. + */ +static VbError_t EcProtectRW(void) +{ + int rv = VbExEcProtectRW(); + + if (rv == VBERROR_EC_REBOOT_TO_RO_REQUIRED) { + VBDEBUG(("VbExEcProtectRW() needs reboot\n")); + } else if (rv != VBERROR_SUCCESS) { + VBDEBUG(("VbExEcProtectRW() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_PROTECT); + } + return rv; } -VbError_t VbEcSoftwareSync(VbCommonParams* cparams) { - VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob; - int in_rw = 0; - int rv; - const uint8_t *ec_hash; - int ec_hash_size; - const uint8_t *expected; - int expected_size; - uint8_t expected_hash[SHA256_DIGEST_SIZE]; - int need_update; - int i; - - /* Determine whether the EC is in RO or RW */ - rv = VbExEcRunningRW(&in_rw); - - if (shared->recovery_reason) { - /* Recovery mode; just verify the EC is in RO code */ - if (rv == VBERROR_SUCCESS && in_rw == 1) { - /* EC is definitely in RW firmware. We want it in read-only code, so - * preserve the current recovery reason and reboot. - * - * We don't reboot on error or unknown EC code, because we could end - * up in an endless reboot loop. If we had some way to track that we'd - * already rebooted for this reason, we could retry only once. */ - VBDEBUG(("VbEcSoftwareSync() - want recovery but got EC-RW\n")); - VbSetRecoveryRequest(shared->recovery_reason); - return VBERROR_EC_REBOOT_TO_RO_REQUIRED; - } - - VBDEBUG(("VbEcSoftwareSync() in recovery; EC-RO\n")); - return VBERROR_SUCCESS; - } - - /* Not in recovery. If we couldn't determine where the EC was, - * reboot to recovery. */ - if (rv != VBERROR_SUCCESS) { - VBDEBUG(("VbEcSoftwareSync() - VbEcSoftwareSync() returned %d\n", rv)); - VbSetRecoveryRequest(VBNV_RECOVERY_EC_UNKNOWN_IMAGE); - return VBERROR_EC_REBOOT_TO_RO_REQUIRED; - } - - /* If AP is read-only normal, EC should be in its RO code also. */ - if (shared->flags & VBSD_LF_USE_RO_NORMAL) { - /* If EC is in RW code, request reboot back to RO */ - if (in_rw == 1) { - VBDEBUG(("VbEcSoftwareSync() - want RO-normal but got EC-RW\n")); - return VBERROR_EC_REBOOT_TO_RO_REQUIRED; - } - - /* Protect the RW flash and stay in EC-RO */ - rv = EcProtectRW(); - if (rv != VBERROR_SUCCESS) - return rv; - - rv = VbExEcStayInRO(); - if (rv != VBERROR_SUCCESS) { - VBDEBUG(("VbEcSoftwareSync() - VbExEcStayInRO() returned %d\n", rv)); - VbSetRecoveryRequest(VBNV_RECOVERY_EC_SOFTWARE_SYNC); - return VBERROR_EC_REBOOT_TO_RO_REQUIRED; - } - - VBDEBUG(("VbEcSoftwareSync() in RO-Normal; EC-RO\n")); - - /* If shutdown is requested, just power the AP back off. This covers the - * case where the lid is closed when then system boots. */ - if (VbExIsShutdownRequested()) { - VBDEBUG(("VbEcSoftwareSync() sees shutdown-requested\n")); - return VBERROR_SHUTDOWN_REQUESTED; - } - - return VBERROR_SUCCESS; - } - - /* Get hash of EC-RW */ - rv = VbExEcHashRW(&ec_hash, &ec_hash_size); - if (rv) { - VBDEBUG(("VbEcSoftwareSync() - VbExEcHashRW() returned %d\n", rv)); - VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_FAILED); - return VBERROR_EC_REBOOT_TO_RO_REQUIRED; - } - if (ec_hash_size != SHA256_DIGEST_SIZE) { - VBDEBUG(("VbEcSoftwareSync() - VbExEcHashRW() says size %d, not %d\n", - ec_hash_size, SHA256_DIGEST_SIZE)); - VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_SIZE); - return VBERROR_EC_REBOOT_TO_RO_REQUIRED; - } - - VBDEBUG(("EC hash:")); - for (i = 0; i < SHA256_DIGEST_SIZE; i++) - VBDEBUG(("%02x", ec_hash[i])); - VBDEBUG(("\n")); - - /* Get expected EC-RW code. Note that we've already checked for RO_NORMAL, - * so we know that the BIOS must be RW-A or RW-B, and therefore the EC must - * match. */ - rv = VbExEcGetExpectedRW( - shared->firmware_index ? VB_SELECT_FIRMWARE_B : VB_SELECT_FIRMWARE_A, - &expected, &expected_size); - if (rv) { - VBDEBUG(("VbEcSoftwareSync() - VbExEcGetExpectedRW() returned %d\n", rv)); - VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_IMAGE); - return VBERROR_EC_REBOOT_TO_RO_REQUIRED; - } - VBDEBUG(("VbEcSoftwareSync() - expected len = %d\n", expected_size)); - - /* Hash expected code */ - internal_SHA256(expected, expected_size, expected_hash); - VBDEBUG(("Expected hash:")); - for (i = 0; i < SHA256_DIGEST_SIZE; i++) - VBDEBUG(("%02x", expected_hash[i])); - VBDEBUG(("\n")); - - need_update = SafeMemcmp(ec_hash, expected_hash, SHA256_DIGEST_SIZE); - - /* TODO: GBB flag to override whether we need update; needed for EC - * development */ - - if (in_rw) { - if (need_update) { - /* EC is running the wrong RW code. Reboot the EC to RO so we can update - * it on the next boot. */ - VBDEBUG(("VbEcSoftwareSync() - in RW, need to update RW, so reboot\n")); - return VBERROR_EC_REBOOT_TO_RO_REQUIRED; - } - - VBDEBUG(("VbEcSoftwareSync() in EC-RW and it matches\n")); - return VBERROR_SUCCESS; - } - - /* Update EC if necessary */ - if (need_update) { - VBDEBUG(("VbEcSoftwareSync() updating EC-RW...\n")); - - if (shared->flags & VBSD_EC_SLOW_UPDATE) { - VBDEBUG(("VbEcSoftwareSync() - EC is slow. Show WAIT screen.\n")); - /* FIXME(crosbug.com/p/12257): Ensure the VGA Option ROM is loaded! */ - VbDisplayScreen(cparams, VB_SCREEN_WAIT, 0, &vnc); - } - - rv = VbExEcUpdateRW(expected, expected_size); - if (rv == VBERROR_EC_REBOOT_TO_RO_REQUIRED) { - /* Reboot required. May need to unprotect RW before updating, - * or may need to reboot after RW updated. Either way, it's not - * an error requiring recovery mode. */ - VBDEBUG(("VbEcSoftwareSync() - VbExEcUpdateRW() needs reboot\n")); - return rv; - } else if (rv != VBERROR_SUCCESS) { - VBDEBUG(("VbEcSoftwareSync() - VbExEcUpdateRW() returned %d\n", rv)); - VbSetRecoveryRequest(VBNV_RECOVERY_EC_UPDATE); - return VBERROR_EC_REBOOT_TO_RO_REQUIRED; - } - - /* - * TODO: should ask EC to recompute its hash to verify it's correct - * before continuing? - */ - } - - /* Protect EC-RW flash */ - rv = EcProtectRW(); - if (rv != VBERROR_SUCCESS) - return rv; - - /* Tell EC to jump to its RW code */ - VBDEBUG(("VbEcSoftwareSync() jumping to EC-RW\n")); - rv = VbExEcJumpToRW(); - if (rv != VBERROR_SUCCESS) { - VBDEBUG(("VbEcSoftwareSync() - VbExEcJumpToRW() returned %d\n", rv)); - VbSetRecoveryRequest(VBNV_RECOVERY_EC_JUMP_RW); - return VBERROR_EC_REBOOT_TO_RO_REQUIRED; - } - - VBDEBUG(("VbEcSoftwareSync() in RW; done\n")); - - /* If shutdown is requested, just power the AP back off. This covers the - * case where the lid is closed when then system boots. */ - if (VbExIsShutdownRequested()) { - VBDEBUG(("VbEcSoftwareSync() sees shutdown-requested\n")); - return VBERROR_SHUTDOWN_REQUESTED; - } - - return VBERROR_SUCCESS; +VbError_t VbEcSoftwareSync(VbCommonParams *cparams) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + int in_rw = 0; + int rv; + const uint8_t *ec_hash = NULL; + int ec_hash_size; + const uint8_t *rw_hash = NULL; + int rw_hash_size; + const uint8_t *expected = NULL; + int expected_size; + uint8_t expected_hash[SHA256_DIGEST_SIZE]; + int need_update = 0; + int i; + + /* Determine whether the EC is in RO or RW */ + rv = VbExEcRunningRW(&in_rw); + + if (shared->recovery_reason) { + /* Recovery mode; just verify the EC is in RO code */ + if (rv == VBERROR_SUCCESS && in_rw == 1) { + /* + * EC is definitely in RW firmware. We want it in + * read-only code, so preserve the current recovery + * reason and reboot. + * + * We don't reboot on error or unknown EC code, because + * we could end up in an endless reboot loop. If we + * had some way to track that we'd already rebooted for + * this reason, we could retry only once. + */ + VBDEBUG(("VbEcSoftwareSync() - " + "want recovery but got EC-RW\n")); + VbSetRecoveryRequest(shared->recovery_reason); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + VBDEBUG(("VbEcSoftwareSync() in recovery; EC-RO\n")); + return VBERROR_SUCCESS; + } + + /* + * Not in recovery. If we couldn't determine where the EC was, + * reboot to recovery. + */ + if (rv != VBERROR_SUCCESS) { + VBDEBUG(("VbEcSoftwareSync() - " + "VbEcEcRunningRW() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_UNKNOWN_IMAGE); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + /* If AP is read-only normal, EC should be in its RO code also. */ + if (shared->flags & VBSD_LF_USE_RO_NORMAL) { + /* If EC is in RW code, request reboot back to RO */ + if (in_rw == 1) { + VBDEBUG(("VbEcSoftwareSync() - " + "want RO-normal but got EC-RW\n")); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + /* Protect the RW flash and stay in EC-RO */ + rv = EcProtectRW(); + if (rv != VBERROR_SUCCESS) + return rv; + + rv = VbExEcStayInRO(); + if (rv != VBERROR_SUCCESS) { + VBDEBUG(("VbEcSoftwareSync() - " + "VbExEcStayInRO() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_SOFTWARE_SYNC); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + VBDEBUG(("VbEcSoftwareSync() in RO-Normal; EC-RO\n")); + + /* + * If shutdown is requested, just power the AP back off. This + * covers the case where the lid is closed when then system + * boots. + */ + if (VbExIsShutdownRequested()) { + VBDEBUG(("VbEcSoftwareSync() " + "sees shutdown-requested\n")); + return VBERROR_SHUTDOWN_REQUESTED; + } + + return VBERROR_SUCCESS; + } + + /* Get hash of EC-RW */ + rv = VbExEcHashRW(&ec_hash, &ec_hash_size); + if (rv) { + VBDEBUG(("VbEcSoftwareSync() - " + "VbExEcHashRW() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_FAILED); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + if (ec_hash_size != SHA256_DIGEST_SIZE) { + VBDEBUG(("VbEcSoftwareSync() - " + "VbExEcHashRW() says size %d, not %d\n", + ec_hash_size, SHA256_DIGEST_SIZE)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_SIZE); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + VBDEBUG(("EC hash:")); + for (i = 0; i < SHA256_DIGEST_SIZE; i++) + VBDEBUG(("%02x", ec_hash[i])); + VBDEBUG(("\n")); + + /* + * Get expected EC-RW hash. Note that we've already checked for + * RO_NORMAL, so we know that the BIOS must be RW-A or RW-B, and + * therefore the EC must match. + */ + rv = VbExEcGetExpectedRWHash(shared->firmware_index ? + VB_SELECT_FIRMWARE_B : VB_SELECT_FIRMWARE_A, + &rw_hash, &rw_hash_size); + + if (rv == VBERROR_EC_GET_EXPECTED_HASH_FROM_IMAGE) { + /* + * BIOS has verified EC image but doesn't have a precomputed + * hash for it, so we must compute the hash ourselves. + */ + rw_hash = NULL; + } else if (rv) { + VBDEBUG(("VbEcSoftwareSync() - " + "VbExEcGetExpectedRWHash() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_HASH); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } else if (rw_hash_size != SHA256_DIGEST_SIZE) { + VBDEBUG(("VbEcSoftwareSync() - " + "VbExEcGetExpectedRWHash() says size %d, not %d\n", + rw_hash_size, SHA256_DIGEST_SIZE)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_HASH); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } else { + VBDEBUG(("Expected hash:")); + for (i = 0; i < SHA256_DIGEST_SIZE; i++) + VBDEBUG(("%02x", rw_hash[i])); + VBDEBUG(("\n")); + + need_update = SafeMemcmp(ec_hash, rw_hash, SHA256_DIGEST_SIZE); + } + + /* + * Get expected EC-RW image if we're sure we need to update (because the + * expected hash didn't match the EC) or we still don't know (because + * there was no expected hash and we need the image to compute one + * ourselves). + */ + if (need_update || !rw_hash) { + /* Get expected EC-RW image */ + rv = VbExEcGetExpectedRW(shared->firmware_index ? + VB_SELECT_FIRMWARE_B : + VB_SELECT_FIRMWARE_A, + &expected, &expected_size); + if (rv) { + VBDEBUG(("VbEcSoftwareSync() - " + "VbExEcGetExpectedRW() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_EXPECTED_IMAGE); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + VBDEBUG(("VbEcSoftwareSync() - expected len = %d\n", + expected_size)); + + /* Hash expected image */ + internal_SHA256(expected, expected_size, expected_hash); + VBDEBUG(("Computed hash of expected image:")); + for (i = 0; i < SHA256_DIGEST_SIZE; i++) + VBDEBUG(("%02x", expected_hash[i])); + VBDEBUG(("\n")); + } + + if (!rw_hash) { + /* + * BIOS didn't have expected EC hash, so check if we need + * update by comparing EC hash to the one we just computed. + */ + need_update = SafeMemcmp(ec_hash, expected_hash, + SHA256_DIGEST_SIZE); + } else if (need_update && + SafeMemcmp(rw_hash, expected_hash, SHA256_DIGEST_SIZE)) { + /* + * We need to update, but the expected EC image doesn't match + * the expected EC hash we were given. + */ + VBDEBUG(("VbEcSoftwareSync() - " + "VbExEcGetExpectedRW() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_HASH_MISMATCH); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + /* + * TODO: GBB flag to override whether we need update; needed for EC + * development. + */ + + if (in_rw) { + if (need_update) { + /* + * EC is running the wrong RW image. Reboot the EC to + * RO so we can update it on the next boot. + */ + VBDEBUG(("VbEcSoftwareSync() - " + "in RW, need to update RW, so reboot\n")); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + VBDEBUG(("VbEcSoftwareSync() in EC-RW and it matches\n")); + + /* + * If shutdown is requested, just power the AP back off. This + * covers the case where the lid is closed when then system + * boots. + */ + if (VbExIsShutdownRequested()) { + VBDEBUG(("VbEcSoftwareSync() " + "sees shutdown-requested\n")); + return VBERROR_SHUTDOWN_REQUESTED; + } + + return VBERROR_SUCCESS; + } + + /* Update EC if necessary */ + if (need_update) { + VBDEBUG(("VbEcSoftwareSync() updating EC-RW...\n")); + + if (shared->flags & VBSD_EC_SLOW_UPDATE) { + VBDEBUG(("VbEcSoftwareSync() - " + "EC is slow. Show WAIT screen.\n")); + /* + * FIXME(crosbug.com/p/12257): Ensure the VGA Option + * ROM is loaded! + */ + VbDisplayScreen(cparams, VB_SCREEN_WAIT, 0, &vnc); + } + + rv = VbExEcUpdateRW(expected, expected_size); + if (rv == VBERROR_EC_REBOOT_TO_RO_REQUIRED) { + /* + * Reboot required. May need to unprotect RW before + * updating, or may need to reboot after RW updated. + * Either way, it's not an error requiring recovery + * mode. + */ + VBDEBUG(("VbEcSoftwareSync() - " + "VbExEcUpdateRW() needs reboot\n")); + return rv; + } else if (rv != VBERROR_SUCCESS) { + VBDEBUG(("VbEcSoftwareSync() - " + "VbExEcUpdateRW() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_UPDATE); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + /* + * TODO: should ask EC to recompute its hash to verify it's + * correct before continuing? + */ + } + + /* Protect EC-RW flash */ + rv = EcProtectRW(); + if (rv != VBERROR_SUCCESS) + return rv; + + /* Tell EC to jump to its RW image */ + VBDEBUG(("VbEcSoftwareSync() jumping to EC-RW\n")); + rv = VbExEcJumpToRW(); + if (rv != VBERROR_SUCCESS) { + VBDEBUG(("VbEcSoftwareSync() - " + "VbExEcJumpToRW() returned %d\n", rv)); + VbSetRecoveryRequest(VBNV_RECOVERY_EC_JUMP_RW); + return VBERROR_EC_REBOOT_TO_RO_REQUIRED; + } + + VBDEBUG(("VbEcSoftwareSync() in RW; done\n")); + + /* + * If shutdown is requested, just power the AP back off. This covers + * the case where the lid is closed when then system boots. + */ + if (VbExIsShutdownRequested()) { + VBDEBUG(("VbEcSoftwareSync() sees shutdown-requested\n")); + return VBERROR_SHUTDOWN_REQUESTED; + } + + return VBERROR_SUCCESS; } - -VbError_t VbSelectAndLoadKernel(VbCommonParams* cparams, - VbSelectAndLoadKernelParams* kparams) { - VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob; - VbError_t retval = VBERROR_SUCCESS; - LoadKernelParams p; - uint32_t tpm_status = 0; - - /* Start timer */ - shared->timer_vb_select_and_load_kernel_enter = VbExGetTimer(); - - VbExNvStorageRead(vnc.raw); - VbNvSetup(&vnc); - - /* Clear output params in case we fail */ - kparams->disk_handle = NULL; - kparams->partition_number = 0; - kparams->bootloader_address = 0; - kparams->bootloader_size = 0; - Memset(kparams->partition_guid, 0, sizeof(kparams->partition_guid)); - - /* Do EC software sync if necessary */ - if (shared->flags & VBSD_EC_SOFTWARE_SYNC) { - retval = VbEcSoftwareSync(cparams); - if (retval != VBERROR_SUCCESS) - goto VbSelectAndLoadKernel_exit; - } - - /* Read the kernel version from the TPM. Ignore errors in recovery mode. */ - tpm_status = RollbackKernelRead(&shared->kernel_version_tpm); - if (0 != tpm_status) { - VBDEBUG(("Unable to get kernel versions from TPM\n")); - if (!shared->recovery_reason) { - VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_R_ERROR); - retval = VBERROR_TPM_READ_KERNEL; - goto VbSelectAndLoadKernel_exit; - } - } - shared->kernel_version_tpm_start = shared->kernel_version_tpm; - - /* Fill in params for calls to LoadKernel() */ - Memset(&p, 0, sizeof(p)); - p.shared_data_blob = cparams->shared_data_blob; - p.shared_data_size = cparams->shared_data_size; - p.gbb_data = cparams->gbb_data; - p.gbb_size = cparams->gbb_size; - - /* - * this could be set to NULL, in which case the vboot header information - * about the load address and size will be used - */ - p.kernel_buffer = kparams->kernel_buffer; - p.kernel_buffer_size = kparams->kernel_buffer_size; - - p.nv_context = &vnc; - p.boot_flags = 0; - if (shared->flags & VBSD_BOOT_DEV_SWITCH_ON) - p.boot_flags |= BOOT_FLAG_DEVELOPER; - - /* Handle separate normal and developer firmware builds. */ +VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams, + VbSelectAndLoadKernelParams *kparams) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + VbError_t retval = VBERROR_SUCCESS; + LoadKernelParams p; + uint32_t tpm_status = 0; + + /* Start timer */ + shared->timer_vb_select_and_load_kernel_enter = VbExGetTimer(); + + VbExNvStorageRead(vnc.raw); + VbNvSetup(&vnc); + + /* Clear output params in case we fail */ + kparams->disk_handle = NULL; + kparams->partition_number = 0; + kparams->bootloader_address = 0; + kparams->bootloader_size = 0; + Memset(kparams->partition_guid, 0, sizeof(kparams->partition_guid)); + + /* Do EC software sync if necessary */ + if (shared->flags & VBSD_EC_SOFTWARE_SYNC) { + retval = VbEcSoftwareSync(cparams); + if (retval != VBERROR_SUCCESS) + goto VbSelectAndLoadKernel_exit; + } + + /* Read kernel version from the TPM. Ignore errors in recovery mode. */ + tpm_status = RollbackKernelRead(&shared->kernel_version_tpm); + if (0 != tpm_status) { + VBDEBUG(("Unable to get kernel versions from TPM\n")); + if (!shared->recovery_reason) { + VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_R_ERROR); + retval = VBERROR_TPM_READ_KERNEL; + goto VbSelectAndLoadKernel_exit; + } + } + shared->kernel_version_tpm_start = shared->kernel_version_tpm; + + /* Fill in params for calls to LoadKernel() */ + Memset(&p, 0, sizeof(p)); + p.shared_data_blob = cparams->shared_data_blob; + p.shared_data_size = cparams->shared_data_size; + p.gbb_data = cparams->gbb_data; + p.gbb_size = cparams->gbb_size; + + /* + * This could be set to NULL, in which case the vboot header + * information about the load address and size will be used. + */ + p.kernel_buffer = kparams->kernel_buffer; + p.kernel_buffer_size = kparams->kernel_buffer_size; + + p.nv_context = &vnc; + p.boot_flags = 0; + if (shared->flags & VBSD_BOOT_DEV_SWITCH_ON) + p.boot_flags |= BOOT_FLAG_DEVELOPER; + + /* Handle separate normal and developer firmware builds. */ #if defined(VBOOT_FIRMWARE_TYPE_NORMAL) - /* Normal-type firmware always acts like the dev switch is off. */ - p.boot_flags &= ~BOOT_FLAG_DEVELOPER; + /* Normal-type firmware always acts like the dev switch is off. */ + p.boot_flags &= ~BOOT_FLAG_DEVELOPER; #elif defined(VBOOT_FIRMWARE_TYPE_DEVELOPER) - /* Developer-type firmware fails if the dev switch is off. */ - if (!(p.boot_flags & BOOT_FLAG_DEVELOPER)) { - /* Dev firmware should be signed with a key that only verifies - * when the dev switch is on, so we should never get here. */ - VBDEBUG(("Developer firmware called with dev switch off!\n")); - VbSetRecoveryRequest(VBNV_RECOVERY_RW_DEV_MISMATCH); - retval = VBERROR_DEV_FIRMWARE_SWITCH_MISMATCH; - goto VbSelectAndLoadKernel_exit; - } + /* Developer-type firmware fails if the dev switch is off. */ + if (!(p.boot_flags & BOOT_FLAG_DEVELOPER)) { + /* + * Dev firmware should be signed with a key that only verifies + * when the dev switch is on, so we should never get here. + */ + VBDEBUG(("Developer firmware called with dev switch off!\n")); + VbSetRecoveryRequest(VBNV_RECOVERY_RW_DEV_MISMATCH); + retval = VBERROR_DEV_FIRMWARE_SWITCH_MISMATCH; + goto VbSelectAndLoadKernel_exit; + } #else - /* Recovery firmware, or merged normal+developer firmware. No - * need to override flags. */ + /* + * Recovery firmware, or merged normal+developer firmware. No need to + * override flags. + */ #endif - /* Select boot path */ - if (shared->recovery_reason) { - /* Recovery boot */ - p.boot_flags |= BOOT_FLAG_RECOVERY; - retval = VbBootRecovery(cparams, &p); - VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, &vnc); - - } else if (p.boot_flags & BOOT_FLAG_DEVELOPER) { - /* Developer boot */ - retval = VbBootDeveloper(cparams, &p); - VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, &vnc); - - } else { - /* Normal boot */ - retval = VbBootNormal(cparams, &p); - - if ((1 == shared->firmware_index) && (shared->flags & VBSD_FWB_TRIED)) { - /* Special cases for when we're trying a new firmware B. These are - * needed because firmware updates also usually change the kernel key, - * which means that the B firmware can only boot a new kernel, and the - * old firmware in A can only boot the previous kernel. */ - - /* Don't advance the TPM if we're trying a new firmware B, because we - * don't yet know if the new kernel will successfully boot. We still - * want to be able to fall back to the previous firmware+kernel if the - * new firmware+kernel fails. */ - - /* If we found only invalid kernels, reboot and try again. This allows - * us to fall back to the previous firmware+kernel instead of giving up - * and going to recovery mode right away. We'll still go to recovery - * mode if we run out of tries and the old firmware can't find a kernel - * it likes. */ - if (VBERROR_INVALID_KERNEL_FOUND == retval) { - VBDEBUG(("Trying firmware B, and only found invalid kernels.\n")); - VbSetRecoveryRequest(VBNV_RECOVERY_NOT_REQUESTED); - goto VbSelectAndLoadKernel_exit; - } - } else { - /* Not trying a new firmware B. */ - /* See if we need to update the TPM. */ - VBDEBUG(("Checking if TPM kernel version needs advancing\n")); - if (shared->kernel_version_tpm > shared->kernel_version_tpm_start) { - tpm_status = RollbackKernelWrite(shared->kernel_version_tpm); - if (0 != tpm_status) { - VBDEBUG(("Error writing kernel versions to TPM.\n")); - VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_W_ERROR); - retval = VBERROR_TPM_WRITE_KERNEL; - goto VbSelectAndLoadKernel_exit; - } - } - } - } - - if (VBERROR_SUCCESS != retval) - goto VbSelectAndLoadKernel_exit; - - /* Save disk parameters */ - kparams->disk_handle = p.disk_handle; - kparams->partition_number = (uint32_t)p.partition_number; - kparams->bootloader_address = p.bootloader_address; - kparams->bootloader_size = (uint32_t)p.bootloader_size; - Memcpy(kparams->partition_guid, p.partition_guid, - sizeof(kparams->partition_guid)); - - /* Lock the kernel versions. Ignore errors in recovery mode. */ - tpm_status = RollbackKernelLock(); - if (0 != tpm_status) { - VBDEBUG(("Error locking kernel versions.\n")); - if (!shared->recovery_reason) { - VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_L_ERROR); - retval = VBERROR_TPM_LOCK_KERNEL; - goto VbSelectAndLoadKernel_exit; - } - } - -VbSelectAndLoadKernel_exit: - - VbNvTeardown(&vnc); - if (vnc.raw_changed) - VbExNvStorageWrite(vnc.raw); - - /* Stop timer */ - shared->timer_vb_select_and_load_kernel_exit = VbExGetTimer(); - - kparams->kernel_buffer = p.kernel_buffer; - kparams->kernel_buffer_size = p.kernel_buffer_size; - - VBDEBUG(("VbSelectAndLoadKernel() returning %d\n", (int)retval)); - - /* Pass through return value from boot path */ - return retval; + /* Select boot path */ + if (shared->recovery_reason) { + /* Recovery boot */ + p.boot_flags |= BOOT_FLAG_RECOVERY; + retval = VbBootRecovery(cparams, &p); + VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, &vnc); + + } else if (p.boot_flags & BOOT_FLAG_DEVELOPER) { + /* Developer boot */ + retval = VbBootDeveloper(cparams, &p); + VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, &vnc); + + } else { + /* Normal boot */ + retval = VbBootNormal(cparams, &p); + + if ((1 == shared->firmware_index) && + (shared->flags & VBSD_FWB_TRIED)) { + /* + * Special cases for when we're trying a new firmware + * B. These are needed because firmware updates also + * usually change the kernel key, which means that the + * B firmware can only boot a new kernel, and the old + * firmware in A can only boot the previous kernel. + * + * Don't advance the TPM if we're trying a new firmware + * B, because we don't yet know if the new kernel will + * successfully boot. We still want to be able to fall + * back to the previous firmware+kernel if the new + * firmware+kernel fails. + * + * If we found only invalid kernels, reboot and try + * again. This allows us to fall back to the previous + * firmware+kernel instead of giving up and going to + * recovery mode right away. We'll still go to + * recovery mode if we run out of tries and the old + * firmware can't find a kernel it likes. + */ + if (VBERROR_INVALID_KERNEL_FOUND == retval) { + VBDEBUG(("Trying firmware B, " + "and only found invalid kernels.\n")); + VbSetRecoveryRequest(VBNV_RECOVERY_NOT_REQUESTED); + goto VbSelectAndLoadKernel_exit; + } + } else { + /* Not trying a new firmware B. */ + + /* See if we need to update the TPM. */ + VBDEBUG(("Checking if TPM kernel version needs " + "advancing\n")); + if (shared->kernel_version_tpm > + shared->kernel_version_tpm_start) { + tpm_status = RollbackKernelWrite( + shared->kernel_version_tpm); + if (0 != tpm_status) { + VBDEBUG(("Error writing kernel " + "versions to TPM.\n")); + VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_W_ERROR); + retval = VBERROR_TPM_WRITE_KERNEL; + goto VbSelectAndLoadKernel_exit; + } + } + } + } + + if (VBERROR_SUCCESS != retval) + goto VbSelectAndLoadKernel_exit; + + /* Save disk parameters */ + kparams->disk_handle = p.disk_handle; + kparams->partition_number = (uint32_t)p.partition_number; + kparams->bootloader_address = p.bootloader_address; + kparams->bootloader_size = (uint32_t)p.bootloader_size; + Memcpy(kparams->partition_guid, p.partition_guid, + sizeof(kparams->partition_guid)); + + /* Lock the kernel versions. Ignore errors in recovery mode. */ + tpm_status = RollbackKernelLock(); + if (0 != tpm_status) { + VBDEBUG(("Error locking kernel versions.\n")); + if (!shared->recovery_reason) { + VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_L_ERROR); + retval = VBERROR_TPM_LOCK_KERNEL; + goto VbSelectAndLoadKernel_exit; + } + } + + VbSelectAndLoadKernel_exit: + + VbNvTeardown(&vnc); + if (vnc.raw_changed) + VbExNvStorageWrite(vnc.raw); + + /* Stop timer */ + shared->timer_vb_select_and_load_kernel_exit = VbExGetTimer(); + + kparams->kernel_buffer = p.kernel_buffer; + kparams->kernel_buffer_size = p.kernel_buffer_size; + + VBDEBUG(("VbSelectAndLoadKernel() returning %d\n", (int)retval)); + + /* Pass through return value from boot path */ + return retval; } diff --git a/firmware/lib/vboot_audio.c b/firmware/lib/vboot_audio.c index 641bae0f..d89bc43f 100644 --- a/firmware/lib/vboot_audio.c +++ b/firmware/lib/vboot_audio.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. * @@ -18,8 +18,10 @@ #define UINT_MAX 4294967295U /* 0xffffffff */ #endif -/* Need one second of noise in the first 22 seconds. - * Total delay >= 30 seconds, <= 60 seconds. */ +/* + * Need one second of noise in the first 22 seconds. + * Total delay >= 30 seconds, <= 60 seconds. + */ #define REQUIRED_NOISE_TIME 1000 #define REQUIRED_NOISE_WITHIN 22000 #define REQUIRED_TOTAL_DELAY 30000 @@ -39,238 +41,261 @@ uint32_t short_count_ = sizeof(short_notes_) / sizeof(VbDevMusicNote); /* No need to dynamically allocate this, is there? */ static VbAudioContext au; - /* Convert from msecs to VbExGetTimer() units. */ static uint64_t ticks_per_msec = 0; /* Initialized by VbAudioOpen() */ static uint64_t VbMsecToTicks(uint16_t msec) { return ticks_per_msec * msec; } -/* Find and return a valid set of note events. We'll use the user's struct - * if possible, but we will still enforce the 30-second timeout and require at - * least a second of audible noise within that period. We allocate storage for - * two reasons: the user's struct will be in flash, which is slow to read, and - * we may need one extra note at the end to pad out the user's notes to a full - * 30 seconds. The caller should free it when finished. +/** + * Find and return a valid set of note events. + * + * We'll use the user's struct if possible, but we will still enforce the + * 30-second timeout and require at least a second of audible noise within that + * period. We allocate storage for two reasons: the user's struct will be in + * flash, which is slow to read, and we may need one extra note at the end to + * pad out the user's notes to a full 30 seconds. The caller should free it + * when finished. */ -static void VbGetDevMusicNotes(VbAudioContext *audio, int use_short) { - VbDevMusicNote *notebuf = 0; - VbDevMusicNote *builtin = 0; - VbDevMusic *hdr = CUSTOM_MUSIC_NOTES; - uint32_t maxsize = CUSTOM_MUSIC_MAXSIZE; /* always <= flash size (8M) */ - uint32_t maxnotes, mysum, mylen, i; - uint32_t this_msecs, on_msecs, total_msecs; - uint32_t count; - - VBDEBUG(("VbGetDevMusicNotes: use_short is %d, hdr is %lx, maxsize is %d\n", - use_short, hdr, maxsize)); - - if (use_short) { - builtin = short_notes_; - count = short_count_; - goto nope; - } - - builtin = default_notes_; - count = default_count_; - - /* If we can't beep in the background, don't allow customization. */ - if (!audio->background_beep) - goto nope; - - if (!hdr || maxsize < sizeof(VbDevMusic)) - goto nope; - - if (0 != Memcmp(hdr->sig, "$SND", sizeof(hdr->sig))) { - VBDEBUG(("VbGetDevMusicNotes: bad sig\n")); - goto nope; - } - - /* How many notes will fit in the flash region? One more than you'd think, - * because there's one note in the header itself. - */ - maxnotes = 1 + (maxsize - sizeof(VbDevMusic)) / sizeof(VbDevMusicNote); - if (hdr->count == 0 || hdr->count > maxnotes) { - VBDEBUG(("VbGetDevMusicNotes: count=%d maxnotes=%d\n", - hdr->count, maxnotes)); - goto nope; - } - - /* CUSTOM_MUSIC_MAXSIZE can't be larger than the size of the flash (around 8M - * or so) so this isn't really necessary, but let's be safe anyway. - */ - if ((sizeof(VbDevMusicNote) > UINT_MAX / hdr->count) || - (sizeof(hdr->count) > UINT_MAX - hdr->count * sizeof(VbDevMusicNote))) { - VBDEBUG(("VbGetDevMusicNotes: count=%d, just isn't right\n")); - goto nope; - } - - /* Now we know this won't overflow */ - mylen = (uint32_t)(sizeof(hdr->count) + hdr->count * sizeof(VbDevMusicNote)); - mysum = Crc32(&(hdr->count), mylen); - - if (mysum != hdr->checksum) { - VBDEBUG(("VbGetDevMusicNotes: mysum=%08x, want=%08x\n", - mysum, hdr->checksum)); - goto nope; - } - - VBDEBUG(("VbGetDevMusicNotes: custom notes struct found at %lx\n", hdr)); - - /* Measure the audible sound up to the first 22 seconds, being careful to - * avoid rollover. The note time is 16 bits, and the note count is 32 bits. - * The product should fit in 64 bits. - */ - total_msecs = 0; - on_msecs = 0; - for (i=0; i < hdr->count; i++) { - this_msecs = hdr->notes[i].msec ; - if (this_msecs) { - total_msecs += this_msecs; - if (total_msecs <= REQUIRED_NOISE_WITHIN && - hdr->notes[i].frequency >= 100 && hdr->notes[i].frequency <= 2000) - on_msecs += this_msecs; - } - } - - /* We require at least one second of noise in the first 22 seconds */ - VBDEBUG(("VbGetDevMusicNotes: with %ld msecs of sound to begin\n", - on_msecs)); - if (on_msecs < REQUIRED_NOISE_TIME) { - goto nope; - } - - /* We'll also require that the total time be less than a minute. No real - * reason, it just gives us less to worry about. - */ - VBDEBUG(("VbGetDevMusicNotes: lasting %ld msecs\n", total_msecs)); - if (total_msecs > MAX_CUSTOM_DELAY) { - goto nope; - } - - /* One more check, just to be paranoid. */ - if (hdr->count > (UINT_MAX / sizeof(VbDevMusicNote) - 1)) { - VBDEBUG(("VbGetDevMusicNotes: they're all out to get me!\n")); - goto nope; - } - - /* Okay, it looks good. Allocate the space (plus one) and copy it over. */ - notebuf = VbExMalloc((hdr->count + 1) * sizeof(VbDevMusicNote)); - Memcpy(notebuf, hdr->notes, hdr->count * sizeof(VbDevMusicNote)); - count = hdr->count; - - /* We also require at least 30 seconds of delay. */ - if (total_msecs < REQUIRED_TOTAL_DELAY) { - /* If the total time is less than 30 seconds, the needed difference will - * fit in 16 bits. - */ - this_msecs = (REQUIRED_TOTAL_DELAY - total_msecs) & 0xffff; - notebuf[hdr->count].msec = this_msecs; - notebuf[hdr->count].frequency = 0; - count++; - VBDEBUG(("VbGetDevMusicNotes: adding %ld msecs of silence\n", - this_msecs)); - } - - /* done */ - audio->music_notes = notebuf; - audio->note_count = count; - audio->free_notes_when_done = 1; - return; - -nope: - /* No custom notes, use the default. The count is already set. */ - VBDEBUG(("VbGetDevMusicNotes: using %d default notes\n", count)); - audio->music_notes = builtin; - audio->note_count = count; - audio->free_notes_when_done = 0; +static void VbGetDevMusicNotes(VbAudioContext *audio, int use_short) +{ + VbDevMusicNote *notebuf = 0; + VbDevMusicNote *builtin = 0; + VbDevMusic *hdr = CUSTOM_MUSIC_NOTES; + uint32_t maxsize = CUSTOM_MUSIC_MAXSIZE; /* always <= flash size (8M) */ + uint32_t maxnotes, mysum, mylen, i; + uint32_t this_msecs, on_msecs, total_msecs; + uint32_t count; + + VBDEBUG(("VbGetDevMusicNotes: use_short is %d, hdr is %lx, " + "maxsize is %d\n", use_short, hdr, maxsize)); + + if (use_short) { + builtin = short_notes_; + count = short_count_; + goto nope; + } + + builtin = default_notes_; + count = default_count_; + + /* If we can't beep in the background, don't allow customization. */ + if (!audio->background_beep) + goto nope; + + if (!hdr || maxsize < sizeof(VbDevMusic)) + goto nope; + + if (0 != Memcmp(hdr->sig, "$SND", sizeof(hdr->sig))) { + VBDEBUG(("VbGetDevMusicNotes: bad sig\n")); + goto nope; + } + + /* + * How many notes will fit in the flash region? One more than you'd + * think, because there's one note in the header itself. + */ + maxnotes = 1 + (maxsize - sizeof(VbDevMusic)) / sizeof(VbDevMusicNote); + if (hdr->count == 0 || hdr->count > maxnotes) { + VBDEBUG(("VbGetDevMusicNotes: count=%d maxnotes=%d\n", + hdr->count, maxnotes)); + goto nope; + } + + /* + * CUSTOM_MUSIC_MAXSIZE can't be larger than the size of the flash + * (around 8M or so) so this isn't really necessary, but let's be safe + * anyway. + */ + if ((sizeof(VbDevMusicNote) > UINT_MAX / hdr->count) || + (sizeof(hdr->count) > + UINT_MAX - hdr->count * sizeof(VbDevMusicNote))) { + VBDEBUG(("VbGetDevMusicNotes: count=%d, just isn't right\n")); + goto nope; + } + + /* Now we know this won't overflow */ + mylen = (uint32_t)(sizeof(hdr->count) + + hdr->count * sizeof(VbDevMusicNote)); + mysum = Crc32(&(hdr->count), mylen); + + if (mysum != hdr->checksum) { + VBDEBUG(("VbGetDevMusicNotes: mysum=%08x, want=%08x\n", + mysum, hdr->checksum)); + goto nope; + } + + VBDEBUG(("VbGetDevMusicNotes: custom notes struct at %lx\n", hdr)); + + /* + * Measure the audible sound up to the first 22 seconds, being careful + * to avoid rollover. The note time is 16 bits, and the note count is + * 32 bits. The product should fit in 64 bits. + */ + total_msecs = 0; + on_msecs = 0; + for (i=0; i < hdr->count; i++) { + this_msecs = hdr->notes[i].msec ; + if (this_msecs) { + total_msecs += this_msecs; + if (total_msecs <= REQUIRED_NOISE_WITHIN && + hdr->notes[i].frequency >= 100 && + hdr->notes[i].frequency <= 2000) + on_msecs += this_msecs; + } + } + + /* We require at least one second of noise in the first 22 seconds */ + VBDEBUG(("VbGetDevMusicNotes: with %ld msecs of sound to begin\n", + on_msecs)); + if (on_msecs < REQUIRED_NOISE_TIME) + goto nope; + + /* + * We'll also require that the total time be less than a minute. No + * real reason, it just gives us less to worry about. + */ + VBDEBUG(("VbGetDevMusicNotes: lasting %ld msecs\n", total_msecs)); + if (total_msecs > MAX_CUSTOM_DELAY) { + goto nope; + } + + /* One more check, just to be paranoid. */ + if (hdr->count > (UINT_MAX / sizeof(VbDevMusicNote) - 1)) { + VBDEBUG(("VbGetDevMusicNotes: they're all out to get me!\n")); + goto nope; + } + + /* Looks good. Allocate the space (plus one) and copy it over. */ + notebuf = VbExMalloc((hdr->count + 1) * sizeof(VbDevMusicNote)); + Memcpy(notebuf, hdr->notes, hdr->count * sizeof(VbDevMusicNote)); + count = hdr->count; + + /* We also require at least 30 seconds of delay. */ + if (total_msecs < REQUIRED_TOTAL_DELAY) { + /* + * If the total time is less than 30 seconds, the needed + * difference will fit in 16 bits. + */ + this_msecs = (REQUIRED_TOTAL_DELAY - total_msecs) & 0xffff; + notebuf[hdr->count].msec = this_msecs; + notebuf[hdr->count].frequency = 0; + count++; + VBDEBUG(("VbGetDevMusicNotes: adding %ld msecs of silence\n", + this_msecs)); + } + + /* Done */ + audio->music_notes = notebuf; + audio->note_count = count; + audio->free_notes_when_done = 1; + return; + + nope: + /* No custom notes, use the default. The count is already set. */ + VBDEBUG(("VbGetDevMusicNotes: using %d default notes\n", count)); + audio->music_notes = builtin; + audio->note_count = count; + audio->free_notes_when_done = 0; } -/* Initialization function. Returns context for processing dev-mode delay */ -VbAudioContext* VbAudioOpen(VbCommonParams* cparams) { - GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; - VbAudioContext* audio = &au; - int use_short = 0; - uint64_t a,b; - - /* Note: may need to allocate things here in future */ - - /* Calibrate audio delay */ - a = VbExGetTimer(); - VbExSleepMs(10); - b = VbExGetTimer(); - ticks_per_msec = (b - a) / 10ULL ; - VBDEBUG(("VbAudioOpen() - ticks_per_msec is %llu\n", ticks_per_msec)); - - /* Initialize */ - Memset(audio, 0, sizeof(*audio)); - audio->background_beep = 1; - audio->play_until = b; /* "zero" starts now */ - - /* See if we have full background sound capability or not. */ - if (VBERROR_SUCCESS != VbExBeep(0,0)) { - VBDEBUG(("VbAudioOpen() - VbExBeep() is limited\n")); - audio->background_beep = 0; - } - - /* Prepare to generate audio/delay event. Use a short developer screen delay - * if indicated by GBB flags. - */ - if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1 - && (gbb->flags & GBB_FLAG_DEV_SCREEN_SHORT_DELAY)) { - VBDEBUG(("VbAudioOpen() - using short developer screen delay\n")); - use_short = 1; - } - - VbGetDevMusicNotes(audio, use_short); - VBDEBUG(("VbAudioOpen() - note count %d\n", audio->note_count)); - - return audio; +/** + * Initialization function. Returns context for processing dev-mode delay. + */ +VbAudioContext *VbAudioOpen(VbCommonParams *cparams) +{ + GoogleBinaryBlockHeader* gbb = + (GoogleBinaryBlockHeader *)cparams->gbb_data; + VbAudioContext *audio = &au; + int use_short = 0; + uint64_t a, b; + + /* Note: may need to allocate things here in future */ + + /* Calibrate audio delay */ + a = VbExGetTimer(); + VbExSleepMs(10); + b = VbExGetTimer(); + ticks_per_msec = (b - a) / 10ULL ; + VBDEBUG(("VbAudioOpen() - ticks_per_msec is %llu\n", ticks_per_msec)); + + /* Initialize */ + Memset(audio, 0, sizeof(*audio)); + audio->background_beep = 1; + audio->play_until = b; /* "zero" starts now */ + + /* See if we have full background sound capability or not. */ + if (VBERROR_SUCCESS != VbExBeep(0,0)) { + VBDEBUG(("VbAudioOpen() - VbExBeep() is limited\n")); + audio->background_beep = 0; + } + + /* + * Prepare to generate audio/delay event. Use a short developer screen + * delay if indicated by GBB flags. + */ + if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1 + && (gbb->flags & GBB_FLAG_DEV_SCREEN_SHORT_DELAY)) { + VBDEBUG(("VbAudioOpen() - using short dev screen delay\n")); + use_short = 1; + } + + VbGetDevMusicNotes(audio, use_short); + VBDEBUG(("VbAudioOpen() - note count %d\n", audio->note_count)); + + return audio; } -/* Caller should loop without extra delay until this returns false */ -int VbAudioLooping(VbAudioContext* audio) { - uint64_t now; - uint16_t freq = audio->current_frequency; - uint16_t msec = 0; - int looping = 1; +/** + * Caller should loop without extra delay until this returns false. + */ +int VbAudioLooping(VbAudioContext *audio) +{ + uint64_t now; + uint16_t freq = audio->current_frequency; + uint16_t msec = 0; + int looping = 1; #if defined(CONFIG_SANDBOX) - return 0; + return 0; #endif - now = VbExGetTimer(); - while (audio->next_note < audio->note_count && now >= audio->play_until) { - freq = audio->music_notes[audio->next_note].frequency; - msec = audio->music_notes[audio->next_note].msec; - audio->play_until += VbMsecToTicks(msec); - audio->next_note++; - } - - if (now >= audio->play_until) { - looping = 0; - freq = 0; - } - - // Do action here. - if (audio->background_beep) { - if (audio->current_frequency != freq) { - VbExBeep(0, freq); - audio->current_frequency = freq; - } - } else if (freq && msec) { - VbExBeep(msec, freq); - now = VbExGetTimer(); - } - - audio->last_time = now; - return looping; + now = VbExGetTimer(); + while (audio->next_note < audio->note_count && + now >= audio->play_until) { + freq = audio->music_notes[audio->next_note].frequency; + msec = audio->music_notes[audio->next_note].msec; + audio->play_until += VbMsecToTicks(msec); + audio->next_note++; + } + + if (now >= audio->play_until) { + looping = 0; + freq = 0; + } + + /* Do action here. */ + if (audio->background_beep) { + if (audio->current_frequency != freq) { + VbExBeep(0, freq); + audio->current_frequency = freq; + } + } else if (freq && msec) { + VbExBeep(msec, freq); + now = VbExGetTimer(); + } + + audio->last_time = now; + return looping; } -/* Caller should call this prior to booting */ -void VbAudioClose(VbAudioContext* audio) { - VbExBeep(0,0); - if (audio->free_notes_when_done) - VbExFree(audio->music_notes); +/** + * Caller should call this prior to booting. + */ +void VbAudioClose(VbAudioContext *audio) +{ + VbExBeep(0,0); + if (audio->free_notes_when_done) + VbExFree(audio->music_notes); } diff --git a/firmware/lib/vboot_display.c b/firmware/lib/vboot_display.c index 929cacc2..7c916d01 100644 --- a/firmware/lib/vboot_display.c +++ b/firmware/lib/vboot_display.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. * @@ -16,671 +16,721 @@ static uint32_t disp_current_screen = VB_SCREEN_BLANK; static uint32_t disp_width = 0, disp_height = 0; - -/* Get the number of localizations in the GBB bitmap data. */ -static VbError_t VbGetLocalizationCount(VbCommonParams* cparams, - uint32_t* count) { - GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; - BmpBlockHeader* hdr; - - /* Default to 0 on error */ - *count = 0; - - /* Make sure the bitmap data is inside the GBB and is non-zero in size */ - if (0 == gbb->bmpfv_size || - gbb->bmpfv_offset > cparams->gbb_size || - gbb->bmpfv_offset + gbb->bmpfv_size > cparams->gbb_size) { - return VBERROR_INVALID_GBB; - } - - /* Sanity-check the bitmap block header */ - hdr = (BmpBlockHeader *)(((uint8_t*)gbb) + gbb->bmpfv_offset); - if ((0 != Memcmp(hdr->signature, BMPBLOCK_SIGNATURE, - BMPBLOCK_SIGNATURE_SIZE)) || - (hdr->major_version > BMPBLOCK_MAJOR_VERSION) || - ((hdr->major_version == BMPBLOCK_MAJOR_VERSION) && - (hdr->minor_version > BMPBLOCK_MINOR_VERSION))) { - return VBERROR_INVALID_BMPFV; - } - - *count = hdr->number_of_localizations; - return VBERROR_SUCCESS; +VbError_t VbGetLocalizationCount(VbCommonParams *cparams, uint32_t *count) +{ + GoogleBinaryBlockHeader *gbb = + (GoogleBinaryBlockHeader *)cparams->gbb_data; + BmpBlockHeader *hdr; + + /* Default to 0 on error */ + *count = 0; + + /* Make sure bitmap data is inside the GBB and is non-zero in size */ + if (0 == gbb->bmpfv_size || + gbb->bmpfv_offset > cparams->gbb_size || + gbb->bmpfv_offset + gbb->bmpfv_size > cparams->gbb_size) { + return VBERROR_INVALID_GBB; + } + + /* Sanity-check the bitmap block header */ + hdr = (BmpBlockHeader *)(((uint8_t *)gbb) + gbb->bmpfv_offset); + if ((0 != Memcmp(hdr->signature, BMPBLOCK_SIGNATURE, + BMPBLOCK_SIGNATURE_SIZE)) || + (hdr->major_version > BMPBLOCK_MAJOR_VERSION) || + ((hdr->major_version == BMPBLOCK_MAJOR_VERSION) && + (hdr->minor_version > BMPBLOCK_MINOR_VERSION))) { + return VBERROR_INVALID_BMPFV; + } + + *count = hdr->number_of_localizations; + return VBERROR_SUCCESS; } +char *VbHWID(VbCommonParams *cparams) +{ + GoogleBinaryBlockHeader *gbb = + (GoogleBinaryBlockHeader *)cparams->gbb_data; + if (0 == gbb->hwid_size || + gbb->hwid_offset > cparams->gbb_size || + gbb->hwid_offset + gbb->hwid_size > cparams->gbb_size) { + VBDEBUG(("VbHWID(): invalid hwid offset/size\n")); + return "{INVALID}"; + } -/* Return a fixed string representing the HWID */ -static char *VbHWID(VbCommonParams* cparams) { - GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; - if (0 == gbb->hwid_size || - gbb->hwid_offset > cparams->gbb_size || - gbb->hwid_offset + gbb->hwid_size > cparams->gbb_size) { - VBDEBUG(("VbHWID(): invalid hwid offset/size\n")); - return "{INVALID}"; - } - return (char*)((uint8_t*)gbb + gbb->hwid_offset); + return (char *)((uint8_t *)gbb + gbb->hwid_offset); } - -/* TODO: We could cache the font info to speed things up, by making the +/* + * TODO: We could cache the font info to speed things up, by making the * in-memory font structure distinct from the in-flash version. We'll do that * Real Soon Now. Until then, we just repeat the same linear search every time. */ typedef FontArrayHeader VbFont_t; -static VbFont_t *VbInternalizeFontData(FontArrayHeader *fonthdr) { - /* Just return the raw data pointer for now. */ - return (VbFont_t *)fonthdr; +VbFont_t *VbInternalizeFontData(FontArrayHeader *fonthdr) +{ + /* Just return the raw data pointer for now. */ + return (VbFont_t *)fonthdr; } -static void VbDoneWithFontForNow(VbFont_t *ptr) { - /* Nothing. */ +void VbDoneWithFontForNow(VbFont_t *ptr) +{ + /* Nothing. */ } -static ImageInfo *VbFindFontGlyph(VbFont_t *font, uint32_t ascii, - void **bufferptr, uint32_t *buffersize) { - uint8_t *ptr, *firstptr; - uint32_t max; - uint32_t i; - FontArrayEntryHeader *entry; - - ptr = (uint8_t *)font; - max = ((FontArrayHeader *)ptr)->num_entries; - ptr += sizeof(FontArrayHeader); - firstptr = ptr; - - /* Simple linear search. */ - for(i=0; i<max; i++) - { - entry = (FontArrayEntryHeader *)ptr; - if (entry->ascii == ascii) { - /* Note: We're assuming the glpyh is uncompressed. That's true - * because the bmpblk_font tool doesn't compress anything. The - * bmpblk_utility does, but it compresses the entire font blob at once, - * and we've already uncompressed that before we got here. - */ - *bufferptr = ptr + sizeof(FontArrayEntryHeader); - *buffersize = entry->info.original_size; - return &(entry->info); - } - ptr += sizeof(FontArrayEntryHeader)+entry->info.compressed_size; - } - - /* NOTE: We must return something valid. We'll just use the first glyph in the - * font structure (so it should be something distinct). - */ - entry = (FontArrayEntryHeader *)firstptr; - *bufferptr = firstptr + sizeof(FontArrayEntryHeader); - *buffersize = entry->info.original_size; - return &(entry->info); +ImageInfo *VbFindFontGlyph(VbFont_t *font, uint32_t ascii, + void **bufferptr, uint32_t *buffersize) +{ + uint8_t *ptr, *firstptr; + uint32_t max; + uint32_t i; + FontArrayEntryHeader *entry; + + ptr = (uint8_t *)font; + max = ((FontArrayHeader *)ptr)->num_entries; + ptr += sizeof(FontArrayHeader); + firstptr = ptr; + + /* + * Simple linear search. + * + * Note: We're assuming glpyhs are uncompressed. That's true because + * the bmpblk_font tool doesn't compress anything. The bmpblk_utility + * does, but it compresses the entire font blob at once, and we've + * already uncompressed that before we got here. + */ + for(i=0; i<max; i++) { + entry = (FontArrayEntryHeader *)ptr; + if (entry->ascii == ascii) { + *bufferptr = ptr + sizeof(FontArrayEntryHeader); + *buffersize = entry->info.original_size; + return &(entry->info); + } + ptr += sizeof(FontArrayEntryHeader)+entry->info.compressed_size; + } + + /* + * We must return something valid. We'll just use the first glyph in + * the font structure (so it should be something distinct). + */ + entry = (FontArrayEntryHeader *)firstptr; + *bufferptr = firstptr + sizeof(FontArrayEntryHeader); + *buffersize = entry->info.original_size; + return &(entry->info); } -/* Try to display the specified text at a particular position. */ -static void VbRenderTextAtPos(char *text, int right_to_left, - uint32_t x, uint32_t y, VbFont_t *font) { - int i; - ImageInfo *image_info = 0; - void *buffer; - uint32_t buffersize; - uint32_t cur_x = x, cur_y = y; - - if (!text || !font) { - VBDEBUG((" VbRenderTextAtPos: invalid args\n")); - return; - } - - for (i=0; text[i]; i++) { - - if (text[i] == '\n') { - if (!image_info) - image_info = VbFindFontGlyph(font, text[i], &buffer, &buffersize); - cur_x = x; - cur_y += image_info->height; - continue; - } - - image_info = VbFindFontGlyph(font, text[i], &buffer, &buffersize); - - if (right_to_left) { - cur_x -= image_info->width; - } - - if (VBERROR_SUCCESS != VbExDisplayImage(cur_x, cur_y, buffer, buffersize)) { - VBDEBUG((" VbRenderTextAtPos: can't display ascii 0x%x\n", text[i])); - } - - if (!right_to_left) { - cur_x += image_info->width; - } - } +void VbRenderTextAtPos(char *text, int right_to_left, + uint32_t x, uint32_t y, VbFont_t *font) +{ + int i; + ImageInfo *image_info = 0; + void *buffer; + uint32_t buffersize; + uint32_t cur_x = x, cur_y = y; + + if (!text || !font) { + VBDEBUG((" VbRenderTextAtPos: invalid args\n")); + return; + } + + for (i=0; text[i]; i++) { + + if (text[i] == '\n') { + if (!image_info) + image_info = VbFindFontGlyph(font, text[i], + &buffer, + &buffersize); + cur_x = x; + cur_y += image_info->height; + continue; + } + + image_info = VbFindFontGlyph(font, text[i], &buffer, + &buffersize); + + if (right_to_left) + cur_x -= image_info->width; + + if (VBERROR_SUCCESS != VbExDisplayImage(cur_x, cur_y, buffer, + buffersize)) { + VBDEBUG((" VbRenderTextAtPos: " + "can't display ascii 0x%x\n", text[i])); + } + + if (!right_to_left) + cur_x += image_info->width; + } } - -/* Display a screen from the GBB. */ #define OUTBUF_LEN 128 -VbError_t VbDisplayScreenFromGBB(VbCommonParams* cparams, uint32_t screen, - VbNvContext *vncptr) { - GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; - static uint8_t* bmpfv; - void* fullimage = NULL; - BmpBlockHeader* hdr; - ScreenLayout* layout; - ImageInfo* image_info; - uint32_t screen_index; - uint32_t localization = 0; - VbError_t retval = VBERROR_UNKNOWN; /* Assume error until proven ok */ - uint32_t inoutsize; - uint32_t offset; - uint32_t i; - VbFont_t *font; - char *text_to_show; - int rtol = 0; - char outbuf[OUTBUF_LEN] = ""; - uint32_t used = 0; - - /* Make sure the bitmap data is inside the GBB and is non-zero in size */ - if (0 == gbb->bmpfv_size || - gbb->bmpfv_offset > cparams->gbb_size || - gbb->bmpfv_offset + gbb->bmpfv_size > cparams->gbb_size) { - VBDEBUG(("VbDisplayScreenFromGBB(): invalid bmpfv offset/size\n")); - return VBERROR_INVALID_GBB; - } - /* Copy bitmap data from GBB into RAM for speed */ - if (!bmpfv) { +VbError_t VbDisplayScreenFromGBB(VbCommonParams *cparams, uint32_t screen, + VbNvContext *vncptr) +{ + GoogleBinaryBlockHeader *gbb = + (GoogleBinaryBlockHeader *)cparams->gbb_data; + static uint8_t *bmpfv; + void *fullimage = NULL; + BmpBlockHeader *hdr; + ScreenLayout *layout; + ImageInfo *image_info; + uint32_t screen_index; + uint32_t localization = 0; + VbError_t retval = VBERROR_UNKNOWN; /* Assume error until proven ok */ + uint32_t inoutsize; + uint32_t offset; + uint32_t i; + VbFont_t *font; + char *text_to_show; + int rtol = 0; + char outbuf[OUTBUF_LEN] = ""; + uint32_t used = 0; + + /* Make sure bitmap data is inside the GBB and is non-zero in size */ + if (0 == gbb->bmpfv_size || + gbb->bmpfv_offset > cparams->gbb_size || + gbb->bmpfv_offset + gbb->bmpfv_size > cparams->gbb_size) { + VBDEBUG(("VbDisplayScreenFromGBB(): " + "invalid bmpfv offset/size\n")); + return VBERROR_INVALID_GBB; + } + + /* Copy bitmap data from GBB into RAM for speed */ + if (!bmpfv) { #ifdef COPY_BMP_DATA - bmpfv = (uint8_t*)VbExMalloc(gbb->bmpfv_size); - Memcpy(bmpfv, ((uint8_t*)gbb) + gbb->bmpfv_offset, gbb->bmpfv_size); + bmpfv = (uint8_t *)VbExMalloc(gbb->bmpfv_size); + Memcpy(bmpfv, ((uint8_t *)gbb) + gbb->bmpfv_offset, + gbb->bmpfv_size); #else - bmpfv = ((uint8_t *)gbb) + gbb->bmpfv_offset; + bmpfv = ((uint8_t *)gbb) + gbb->bmpfv_offset; #endif - } - - /* Sanity-check the bitmap block header */ - hdr = (BmpBlockHeader *)bmpfv; - if ((0 != Memcmp(hdr->signature, BMPBLOCK_SIGNATURE, - BMPBLOCK_SIGNATURE_SIZE)) || - (hdr->major_version > BMPBLOCK_MAJOR_VERSION) || - ((hdr->major_version == BMPBLOCK_MAJOR_VERSION) && - (hdr->minor_version > BMPBLOCK_MINOR_VERSION))) { - VBDEBUG(("VbDisplayScreenFromGBB(): invalid/too new bitmap header\n")); - retval = VBERROR_INVALID_BMPFV; - goto VbDisplayScreenFromGBB_exit; - } - - /* Translate screen ID into index. Note that not all screens are in the - * GBB. */ - /* TODO: ensure screen IDs match indices? Having this translation - * here is awful. */ - switch (screen) { - case VB_SCREEN_DEVELOPER_WARNING: - screen_index = SCREEN_DEVELOPER_WARNING; - break; - case VB_SCREEN_RECOVERY_REMOVE: - screen_index = SCREEN_RECOVERY_REMOVE; - break; - case VB_SCREEN_RECOVERY_NO_GOOD: - screen_index = SCREEN_RECOVERY_NO_GOOD; - break; - case VB_SCREEN_RECOVERY_INSERT: - screen_index = SCREEN_RECOVERY_INSERT; - break; - case VB_SCREEN_RECOVERY_TO_DEV: - screen_index = SCREEN_RECOVERY_TO_DEV; - break; - case VB_SCREEN_DEVELOPER_TO_NORM: - screen_index = SCREEN_DEVELOPER_TO_NORM; - break; - case VB_SCREEN_WAIT: - screen_index = SCREEN_WAIT; - break; - case VB_SCREEN_TO_NORM_CONFIRMED: - screen_index = SCREEN_TO_NORM_CONFIRMED; - break; - case VB_SCREEN_BLANK: - case VB_SCREEN_DEVELOPER_EGG: - default: - /* Screens which aren't in the GBB */ - VBDEBUG(("VbDisplayScreenFromGBB(): screen %d not in the GBB\n", - (int)screen)); - retval = VBERROR_INVALID_SCREEN_INDEX; - goto VbDisplayScreenFromGBB_exit; - } - if (screen_index >= hdr->number_of_screenlayouts) { - VBDEBUG(("VbDisplayScreenFromGBB(): screen %d index %d not in the GBB\n", - (int)screen, (int)screen_index)); - retval = VBERROR_INVALID_SCREEN_INDEX; - goto VbDisplayScreenFromGBB_exit; - } - - /* Clip localization to the number of localizations present in the GBB */ - VbNvGet(vncptr, VBNV_LOCALIZATION_INDEX, &localization); - if (localization >= hdr->number_of_localizations) { - localization = 0; - VbNvSet(vncptr, VBNV_LOCALIZATION_INDEX, localization); - } - - /* Calculate offset of screen layout = start of screen stuff + - * correct locale + correct screen. */ - offset = sizeof(BmpBlockHeader) + - localization * hdr->number_of_screenlayouts * sizeof(ScreenLayout) + - screen_index * sizeof(ScreenLayout); - layout = (ScreenLayout*)(bmpfv + offset); - - /* Display all bitmaps for the image */ - for (i = 0; i < MAX_IMAGE_IN_LAYOUT; i++) { - if (layout->images[i].image_info_offset) { - offset = layout->images[i].image_info_offset; - image_info = (ImageInfo*)(bmpfv + offset); - fullimage = bmpfv + offset + sizeof(ImageInfo); - inoutsize = image_info->original_size; - if (inoutsize && image_info->compression != COMPRESS_NONE) { - fullimage = VbExMalloc(inoutsize); - retval = VbExDecompress(bmpfv + offset + sizeof(ImageInfo), - image_info->compressed_size, - image_info->compression, - fullimage, &inoutsize); - if (VBERROR_SUCCESS != retval) { - VbExFree(fullimage); - goto VbDisplayScreenFromGBB_exit; - } - } - - switch(image_info->format) { - case FORMAT_BMP: - retval = VbExDisplayImage(layout->images[i].x, layout->images[i].y, - fullimage, inoutsize); - break; - - case FORMAT_FONT: - /* The uncompressed blob is our font structure. Cache it as needed. */ - font = VbInternalizeFontData(fullimage); - - /* TODO: handle text in general here */ - if (TAG_HWID == image_info->tag || TAG_HWID_RTOL == image_info->tag) { - text_to_show = VbHWID(cparams); - rtol = (TAG_HWID_RTOL == image_info->tag); - } else { - text_to_show = ""; - rtol = 0; - } - - VbRenderTextAtPos(text_to_show, rtol, - layout->images[i].x, layout->images[i].y, font); - - VbDoneWithFontForNow(font); - break; - - default: - VBDEBUG(("VbDisplayScreenFromGBB(): unsupported ImageFormat %d\n", - image_info->format)); - retval = VBERROR_INVALID_GBB; - } - - if (COMPRESS_NONE != image_info->compression) - VbExFree(fullimage); - - if (VBERROR_SUCCESS != retval) - goto VbDisplayScreenFromGBB_exit; - - } - } - - /* Successful if all bitmaps displayed */ - retval = VBERROR_SUCCESS; - - /* If GBB flags is nonzero, complain because that's something that the - * factory MUST fix before shipping. We only have to do this here, because - * it's obvious that something is wrong if we're not displaying screens from - * the GBB. - */ - if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1 && - (gbb->flags != 0)) { - used += Strncat(outbuf + used, "gbb.flags is nonzero: 0x", - OUTBUF_LEN - used); - used += Uint64ToString(outbuf + used, OUTBUF_LEN - used, gbb->flags, 16, 8); - used += Strncat(outbuf + used, "\n", OUTBUF_LEN - used); - (void)VbExDisplayDebugInfo(outbuf); - } - - -VbDisplayScreenFromGBB_exit: - - VBDEBUG(("leaving VbDisplayScreenFromGBB() with %d\n",retval)); - return retval; + } + + /* Sanity-check the bitmap block header */ + hdr = (BmpBlockHeader *)bmpfv; + if ((0 != Memcmp(hdr->signature, BMPBLOCK_SIGNATURE, + BMPBLOCK_SIGNATURE_SIZE)) || + (hdr->major_version > BMPBLOCK_MAJOR_VERSION) || + ((hdr->major_version == BMPBLOCK_MAJOR_VERSION) && + (hdr->minor_version > BMPBLOCK_MINOR_VERSION))) { + VBDEBUG(("VbDisplayScreenFromGBB(): " + "invalid/too new bitmap header\n")); + retval = VBERROR_INVALID_BMPFV; + goto VbDisplayScreenFromGBB_exit; + } + + /* + * Translate screen ID into index. Note that not all screens are in + * the GBB. + * + * TODO: ensure screen IDs match indices? Having this translation here + * is awful. + */ + switch (screen) { + case VB_SCREEN_DEVELOPER_WARNING: + screen_index = SCREEN_DEVELOPER_WARNING; + break; + case VB_SCREEN_RECOVERY_REMOVE: + screen_index = SCREEN_RECOVERY_REMOVE; + break; + case VB_SCREEN_RECOVERY_NO_GOOD: + screen_index = SCREEN_RECOVERY_NO_GOOD; + break; + case VB_SCREEN_RECOVERY_INSERT: + screen_index = SCREEN_RECOVERY_INSERT; + break; + case VB_SCREEN_RECOVERY_TO_DEV: + screen_index = SCREEN_RECOVERY_TO_DEV; + break; + case VB_SCREEN_DEVELOPER_TO_NORM: + screen_index = SCREEN_DEVELOPER_TO_NORM; + break; + case VB_SCREEN_WAIT: + screen_index = SCREEN_WAIT; + break; + case VB_SCREEN_TO_NORM_CONFIRMED: + screen_index = SCREEN_TO_NORM_CONFIRMED; + break; + case VB_SCREEN_BLANK: + case VB_SCREEN_DEVELOPER_EGG: + default: + /* Screens which aren't in the GBB */ + VBDEBUG(("VbDisplayScreenFromGBB(): screen %d not in the GBB\n", + (int)screen)); + retval = VBERROR_INVALID_SCREEN_INDEX; + goto VbDisplayScreenFromGBB_exit; + } + + if (screen_index >= hdr->number_of_screenlayouts) { + VBDEBUG(("VbDisplayScreenFromGBB(): " + "screen %d index %d not in the GBB\n", + (int)screen, (int)screen_index)); + retval = VBERROR_INVALID_SCREEN_INDEX; + goto VbDisplayScreenFromGBB_exit; + } + + /* Clip localization to number of localizations present in the GBB */ + VbNvGet(vncptr, VBNV_LOCALIZATION_INDEX, &localization); + if (localization >= hdr->number_of_localizations) { + localization = 0; + VbNvSet(vncptr, VBNV_LOCALIZATION_INDEX, localization); + } + + /* + * Calculate offset of screen layout = start of screen stuff + correct + * locale + correct screen. + */ + offset = sizeof(BmpBlockHeader) + + localization * hdr->number_of_screenlayouts * + sizeof(ScreenLayout) + + screen_index * sizeof(ScreenLayout); + layout = (ScreenLayout *)(bmpfv + offset); + + /* Display all bitmaps for the image */ + for (i = 0; i < MAX_IMAGE_IN_LAYOUT; i++) { + if (!layout->images[i].image_info_offset) + continue; + + offset = layout->images[i].image_info_offset; + image_info = (ImageInfo *)(bmpfv + offset); + fullimage = bmpfv + offset + sizeof(ImageInfo); + inoutsize = image_info->original_size; + if (inoutsize && + image_info->compression != COMPRESS_NONE) { + fullimage = VbExMalloc(inoutsize); + retval = VbExDecompress( + bmpfv + offset + sizeof(ImageInfo), + image_info->compressed_size, + image_info->compression, + fullimage, &inoutsize); + if (VBERROR_SUCCESS != retval) { + VbExFree(fullimage); + goto VbDisplayScreenFromGBB_exit; + } + } + + switch(image_info->format) { + case FORMAT_BMP: + retval = VbExDisplayImage(layout->images[i].x, + layout->images[i].y, + fullimage, inoutsize); + break; + + case FORMAT_FONT: + /* + * The uncompressed blob is our font structure. Cache + * it as needed. + */ + font = VbInternalizeFontData(fullimage); + + /* TODO: handle text in general here */ + if (TAG_HWID == image_info->tag || + TAG_HWID_RTOL == image_info->tag) { + text_to_show = VbHWID(cparams); + rtol = (TAG_HWID_RTOL == image_info->tag); + } else { + text_to_show = ""; + rtol = 0; + } + + VbRenderTextAtPos(text_to_show, rtol, + layout->images[i].x, + layout->images[i].y, font); + + VbDoneWithFontForNow(font); + break; + + default: + VBDEBUG(("VbDisplayScreenFromGBB(): " + "unsupported ImageFormat %d\n", + image_info->format)); + retval = VBERROR_INVALID_GBB; + } + + if (COMPRESS_NONE != image_info->compression) + VbExFree(fullimage); + + if (VBERROR_SUCCESS != retval) + goto VbDisplayScreenFromGBB_exit; + } + + /* Successful if all bitmaps displayed */ + retval = VBERROR_SUCCESS; + + /* + * If GBB flags is nonzero, complain because that's something that the + * factory MUST fix before shipping. We only have to do this here, + * because it's obvious that something is wrong if we're not displaying + * screens from the GBB. + */ + if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1 && + (gbb->flags != 0)) { + used += Strncat(outbuf + used, "gbb.flags is nonzero: 0x", + OUTBUF_LEN - used); + used += Uint64ToString(outbuf + used, OUTBUF_LEN - used, + gbb->flags, 16, 8); + used += Strncat(outbuf + used, "\n", OUTBUF_LEN - used); + (void)VbExDisplayDebugInfo(outbuf); + } + + VbDisplayScreenFromGBB_exit: + VBDEBUG(("leaving VbDisplayScreenFromGBB() with %d\n",retval)); + return retval; } +VbError_t VbDisplayScreen(VbCommonParams *cparams, uint32_t screen, int force, + VbNvContext *vncptr) +{ + VbError_t retval; -/* Display a screen, initializing the display if necessary. If force!=0, - * redisplays the screen even if it's the same as the current screen. */ -VbError_t VbDisplayScreen(VbCommonParams* cparams, uint32_t screen, int force, - VbNvContext *vncptr) { - VbError_t retval; - - /* Initialize display if necessary */ - if (!disp_width) { - retval = VbExDisplayInit(&disp_width, &disp_height); - if (VBERROR_SUCCESS != retval) - return retval; - } + /* Initialize display if necessary */ + if (!disp_width) { + retval = VbExDisplayInit(&disp_width, &disp_height); + if (VBERROR_SUCCESS != retval) + return retval; + } - /* If the requested screen is the same as the current one, we're done. */ - if (disp_current_screen == screen && 0 == force) - return VBERROR_SUCCESS; + /* If requested screen is the same as the current one, we're done. */ + if (disp_current_screen == screen && 0 == force) + return VBERROR_SUCCESS; - /* If the screen is blank, turn off the backlight; else turn it on. */ - VbExDisplayBacklight(VB_SCREEN_BLANK == screen ? 0 : 1); + /* If the screen is blank, turn off the backlight; else turn it on. */ + VbExDisplayBacklight(VB_SCREEN_BLANK == screen ? 0 : 1); - /* Request the screen */ - disp_current_screen = screen; + /* Request the screen */ + disp_current_screen = screen; - /* Look in the GBB first */ - if (VBERROR_SUCCESS == VbDisplayScreenFromGBB(cparams, screen, vncptr)) - return VBERROR_SUCCESS; + /* Look in the GBB first */ + if (VBERROR_SUCCESS == VbDisplayScreenFromGBB(cparams, screen, vncptr)) + return VBERROR_SUCCESS; - /* If the screen wasn't in the GBB bitmaps, fall back to a default screen. */ - return VbExDisplayScreen(screen); + /* If screen wasn't in the GBB bitmaps, fall back to a default */ + return VbExDisplayScreen(screen); } - -static void Uint8ToString(char *buf, uint8_t val) { - const char *trans = "0123456789abcdef"; - *buf++ = trans[val >> 4]; - *buf = trans[val & 0xF]; +static void Uint8ToString(char *buf, uint8_t val) +{ + const char *trans = "0123456789abcdef"; + *buf++ = trans[val >> 4]; + *buf = trans[val & 0xF]; } -static void FillInSha1Sum(char *outbuf, VbPublicKey* key) { - uint8_t* buf = ((uint8_t *)key) + key->key_offset; - uint64_t buflen = key->key_size; - uint8_t* digest = DigestBuf(buf, buflen, SHA1_DIGEST_ALGORITHM); - int i; - for (i=0; i<SHA1_DIGEST_SIZE; i++) { - Uint8ToString(outbuf, digest[i]); - outbuf += 2; - } - *outbuf = '\0'; - VbExFree(digest); +static void FillInSha1Sum(char *outbuf, VbPublicKey *key) +{ + uint8_t *buf = ((uint8_t *)key) + key->key_offset; + uint64_t buflen = key->key_size; + uint8_t *digest = DigestBuf(buf, buflen, SHA1_DIGEST_ALGORITHM); + int i; + for (i = 0; i < SHA1_DIGEST_SIZE; i++) { + Uint8ToString(outbuf, digest[i]); + outbuf += 2; + } + *outbuf = '\0'; + VbExFree(digest); } -const char *RecoveryReasonString(uint8_t code) { - switch(code) { - case VBNV_RECOVERY_NOT_REQUESTED: - return "Recovery not requested"; - case VBNV_RECOVERY_LEGACY: - return "Recovery requested from legacy utility"; - case VBNV_RECOVERY_RO_MANUAL: - return "recovery button pressed"; - case VBNV_RECOVERY_RO_INVALID_RW: - return "RW firmware failed signature check"; - case VBNV_RECOVERY_RO_S3_RESUME: - return "S3 resume failed"; - case VBNV_RECOVERY_DEP_RO_TPM_ERROR: - return "TPM error in read-only firmware"; - case VBNV_RECOVERY_RO_SHARED_DATA: - return "Shared data error in read-only firmware"; - case VBNV_RECOVERY_RO_TEST_S3: - return "Test error from S3Resume()"; - case VBNV_RECOVERY_RO_TEST_LFS: - return "Test error from LoadFirmwareSetup()"; - case VBNV_RECOVERY_RO_TEST_LF: - return "Test error from LoadFirmware()"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_NOT_DONE: - return "RW firmware check not done"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_DEV_MISMATCH: - return "RW firmware developer flag mismatch"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_REC_MISMATCH: - return "RW firmware recovery flag mismatch"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_VERIFY_KEYBLOCK: - return "RW firmware unable to verify key block"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_KEY_ROLLBACK: - return "RW firmware key version rollback detected"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_DATA_KEY_PARSE: - return "RW firmware unable to parse data key"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_VERIFY_PREAMBLE: - return "RW firmware unable to verify preamble"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_FW_ROLLBACK: - return "RW firmware version rollback detected"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_GET_FW_BODY: - return "RW firmware unable to get firmware body"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_HASH_WRONG_SIZE: - return "RW firmware hash is wrong size"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_VERIFY_BODY: - return "RW firmware unable to verify firmware body"; - case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_NO_RO_NORMAL: - return "RW firmware read-only normal path is not supported"; - case VBNV_RECOVERY_RO_FIRMWARE: - return "Firmware problem outside of verified boot"; - case VBNV_RECOVERY_RO_TPM_REBOOT: - return "TPM requires a system reboot (should be transient)"; - case VBNV_RECOVERY_EC_SOFTWARE_SYNC: - return "EC software sync error"; - case VBNV_RECOVERY_EC_UNKNOWN_IMAGE: - return "EC software sync unable to determine active EC image"; - case VBNV_RECOVERY_DEP_EC_HASH: - return "EC software sync error obtaining EC image hash"; - case VBNV_RECOVERY_EC_EXPECTED_IMAGE: - return "EC software sync error obtaining expected EC image from BIOS"; - case VBNV_RECOVERY_EC_UPDATE: - return "EC software sync error updating EC"; - case VBNV_RECOVERY_EC_JUMP_RW: - return "EC software sync unable to jump to EC-RW"; - case VBNV_RECOVERY_EC_PROTECT: - return "EC software sync protection error"; - case VBNV_RECOVERY_RO_UNSPECIFIED: - return "Unspecified/unknown error in RO firmware"; - case VBNV_RECOVERY_RW_DEV_SCREEN: - return "User requested recovery from dev-mode warning screen"; - case VBNV_RECOVERY_RW_NO_OS: - return "No OS kernel detected (or kernel rollback attempt?)"; - case VBNV_RECOVERY_RW_INVALID_OS: - return "OS kernel failed signature check"; - case VBNV_RECOVERY_DEP_RW_TPM_ERROR: - return "TPM error in rewritable firmware"; - case VBNV_RECOVERY_RW_DEV_MISMATCH: - return "RW firmware in dev mode, but dev switch is off"; - case VBNV_RECOVERY_RW_SHARED_DATA: - return "Shared data error in rewritable firmware"; - case VBNV_RECOVERY_RW_TEST_LK: - return "Test error from LoadKernel()"; - case VBNV_RECOVERY_DEP_RW_NO_DISK: - return "No bootable disk found"; - case VBNV_RECOVERY_TPM_E_FAIL: - return "TPM error that was not fixed by reboot"; - case VBNV_RECOVERY_RO_TPM_S_ERROR: - return "TPM setup error in read-only firmware"; - case VBNV_RECOVERY_RO_TPM_W_ERROR: - return "TPM write error in read-only firmware"; - case VBNV_RECOVERY_RO_TPM_L_ERROR: - return "TPM lock error in read-only firmware"; - case VBNV_RECOVERY_RO_TPM_U_ERROR: - return "TPM update error in read-only firmware"; - case VBNV_RECOVERY_RW_TPM_R_ERROR: - return "TPM read error in rewritable firmware"; - case VBNV_RECOVERY_RW_TPM_W_ERROR: - return "TPM write error in rewritable firmware"; - case VBNV_RECOVERY_RW_TPM_L_ERROR: - return "TPM lock error in rewritable firmware"; - case VBNV_RECOVERY_EC_HASH_FAILED: - return "EC software sync unable to get EC image hash"; - case VBNV_RECOVERY_EC_HASH_SIZE: - return "EC software sync invalid image hash size"; - case VBNV_RECOVERY_LK_UNSPECIFIED: - return "Unspecified error while trying to load kernel"; - case VBNV_RECOVERY_RW_NO_DISK: - return "No bootable storage device in system"; - case VBNV_RECOVERY_RW_NO_KERNEL: - return "No bootable kernel found on disk"; - case VBNV_RECOVERY_RW_UNSPECIFIED: - return "Unspecified/unknown error in RW firmware"; - case VBNV_RECOVERY_KE_DM_VERITY: - return "DM-verity error"; - case VBNV_RECOVERY_KE_UNSPECIFIED: - return "Unspecified/unknown error in kernel"; - case VBNV_RECOVERY_US_TEST: - return "Recovery mode test from user-mode"; - case VBNV_RECOVERY_US_UNSPECIFIED: - return "Unspecified/unknown error in user-mode"; - } - return "We have no idea what this means"; +const char *RecoveryReasonString(uint8_t code) +{ + switch(code) { + case VBNV_RECOVERY_NOT_REQUESTED: + return "Recovery not requested"; + case VBNV_RECOVERY_LEGACY: + return "Recovery requested from legacy utility"; + case VBNV_RECOVERY_RO_MANUAL: + return "recovery button pressed"; + case VBNV_RECOVERY_RO_INVALID_RW: + return "RW firmware failed signature check"; + case VBNV_RECOVERY_RO_S3_RESUME: + return "S3 resume failed"; + case VBNV_RECOVERY_DEP_RO_TPM_ERROR: + return "TPM error in read-only firmware"; + case VBNV_RECOVERY_RO_SHARED_DATA: + return "Shared data error in read-only firmware"; + case VBNV_RECOVERY_RO_TEST_S3: + return "Test error from S3Resume()"; + case VBNV_RECOVERY_RO_TEST_LFS: + return "Test error from LoadFirmwareSetup()"; + case VBNV_RECOVERY_RO_TEST_LF: + return "Test error from LoadFirmware()"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_NOT_DONE: + return "RW firmware check not done"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_DEV_MISMATCH: + return "RW firmware developer flag mismatch"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_REC_MISMATCH: + return "RW firmware recovery flag mismatch"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + + VBSD_LF_CHECK_VERIFY_KEYBLOCK: + return "RW firmware unable to verify key block"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_KEY_ROLLBACK: + return "RW firmware key version rollback detected"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + + VBSD_LF_CHECK_DATA_KEY_PARSE: + return "RW firmware unable to parse data key"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + + VBSD_LF_CHECK_VERIFY_PREAMBLE: + return "RW firmware unable to verify preamble"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_FW_ROLLBACK: + return "RW firmware version rollback detected"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_GET_FW_BODY: + return "RW firmware unable to get firmware body"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + + VBSD_LF_CHECK_HASH_WRONG_SIZE: + return "RW firmware hash is wrong size"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_VERIFY_BODY: + return "RW firmware unable to verify firmware body"; + case VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN + VBSD_LF_CHECK_NO_RO_NORMAL: + return "RW firmware read-only normal path is not supported"; + case VBNV_RECOVERY_RO_FIRMWARE: + return "Firmware problem outside of verified boot"; + case VBNV_RECOVERY_RO_TPM_REBOOT: + return "TPM requires a system reboot (should be transient)"; + case VBNV_RECOVERY_EC_SOFTWARE_SYNC: + return "EC software sync error"; + case VBNV_RECOVERY_EC_UNKNOWN_IMAGE: + return "EC software sync unable to determine active EC image"; + case VBNV_RECOVERY_DEP_EC_HASH: + return "EC software sync error obtaining EC image hash"; + case VBNV_RECOVERY_EC_EXPECTED_IMAGE: + return "EC software sync error " + "obtaining expected EC image from BIOS"; + case VBNV_RECOVERY_EC_EXPECTED_HASH: + return "EC software sync error " + "obtaining expected EC hash from BIOS"; + case VBNV_RECOVERY_EC_HASH_MISMATCH: + return "EC software sync error " + "comparing expected EC hash and image"; + case VBNV_RECOVERY_EC_UPDATE: + return "EC software sync error updating EC"; + case VBNV_RECOVERY_EC_JUMP_RW: + return "EC software sync unable to jump to EC-RW"; + case VBNV_RECOVERY_EC_PROTECT: + return "EC software sync protection error"; + case VBNV_RECOVERY_RO_UNSPECIFIED: + return "Unspecified/unknown error in RO firmware"; + case VBNV_RECOVERY_RW_DEV_SCREEN: + return "User requested recovery from dev-mode warning screen"; + case VBNV_RECOVERY_RW_NO_OS: + return "No OS kernel detected (or kernel rollback attempt?)"; + case VBNV_RECOVERY_RW_INVALID_OS: + return "OS kernel failed signature check"; + case VBNV_RECOVERY_DEP_RW_TPM_ERROR: + return "TPM error in rewritable firmware"; + case VBNV_RECOVERY_RW_DEV_MISMATCH: + return "RW firmware in dev mode, but dev switch is off"; + case VBNV_RECOVERY_RW_SHARED_DATA: + return "Shared data error in rewritable firmware"; + case VBNV_RECOVERY_RW_TEST_LK: + return "Test error from LoadKernel()"; + case VBNV_RECOVERY_DEP_RW_NO_DISK: + return "No bootable disk found"; + case VBNV_RECOVERY_TPM_E_FAIL: + return "TPM error that was not fixed by reboot"; + case VBNV_RECOVERY_RO_TPM_S_ERROR: + return "TPM setup error in read-only firmware"; + case VBNV_RECOVERY_RO_TPM_W_ERROR: + return "TPM write error in read-only firmware"; + case VBNV_RECOVERY_RO_TPM_L_ERROR: + return "TPM lock error in read-only firmware"; + case VBNV_RECOVERY_RO_TPM_U_ERROR: + return "TPM update error in read-only firmware"; + case VBNV_RECOVERY_RW_TPM_R_ERROR: + return "TPM read error in rewritable firmware"; + case VBNV_RECOVERY_RW_TPM_W_ERROR: + return "TPM write error in rewritable firmware"; + case VBNV_RECOVERY_RW_TPM_L_ERROR: + return "TPM lock error in rewritable firmware"; + case VBNV_RECOVERY_EC_HASH_FAILED: + return "EC software sync unable to get EC image hash"; + case VBNV_RECOVERY_EC_HASH_SIZE: + return "EC software sync invalid image hash size"; + case VBNV_RECOVERY_LK_UNSPECIFIED: + return "Unspecified error while trying to load kernel"; + case VBNV_RECOVERY_RW_NO_DISK: + return "No bootable storage device in system"; + case VBNV_RECOVERY_RW_NO_KERNEL: + return "No bootable kernel found on disk"; + case VBNV_RECOVERY_RW_UNSPECIFIED: + return "Unspecified/unknown error in RW firmware"; + case VBNV_RECOVERY_KE_DM_VERITY: + return "DM-verity error"; + case VBNV_RECOVERY_KE_UNSPECIFIED: + return "Unspecified/unknown error in kernel"; + case VBNV_RECOVERY_US_TEST: + return "Recovery mode test from user-mode"; + case VBNV_RECOVERY_US_UNSPECIFIED: + return "Unspecified/unknown error in user-mode"; + } + return "We have no idea what this means"; } - #define DEBUG_INFO_SIZE 512 -/* Display debug info to the screen */ -VbError_t VbDisplayDebugInfo(VbCommonParams* cparams, VbNvContext *vncptr) { - VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob; - GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; - char buf[DEBUG_INFO_SIZE] = ""; - char sha1sum[SHA1_DIGEST_SIZE * 2 + 1]; - uint32_t used = 0; - uint32_t i; - - /* Redisplay the current screen, to overwrite any previous debug output */ - VbDisplayScreen(cparams, disp_current_screen, 1, vncptr); - - /* Add hardware ID */ - used += Strncat(buf + used, "HWID: ", DEBUG_INFO_SIZE - used); - if (0 == gbb->hwid_size || - gbb->hwid_offset > cparams->gbb_size || - gbb->hwid_offset + gbb->hwid_size > cparams->gbb_size) { - VBDEBUG(("VbDisplayDebugInfo(): invalid hwid offset/size\n")); - used += Strncat(buf + used, "(INVALID)", DEBUG_INFO_SIZE - used); +VbError_t VbDisplayDebugInfo(VbCommonParams *cparams, VbNvContext *vncptr) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + GoogleBinaryBlockHeader *gbb = + (GoogleBinaryBlockHeader *)cparams->gbb_data; + char buf[DEBUG_INFO_SIZE] = ""; + char sha1sum[SHA1_DIGEST_SIZE * 2 + 1]; + uint32_t used = 0; + uint32_t i; + + /* Redisplay current screen to overwrite any previous debug output */ + VbDisplayScreen(cparams, disp_current_screen, 1, vncptr); + + /* Add hardware ID */ + used += Strncat(buf + used, "HWID: ", DEBUG_INFO_SIZE - used); + if (0 == gbb->hwid_size || + gbb->hwid_offset > cparams->gbb_size || + gbb->hwid_offset + gbb->hwid_size > cparams->gbb_size) { + VBDEBUG(("VbDisplayDebugInfo(): invalid hwid offset/size\n")); + used += Strncat(buf + used, + "(INVALID)", DEBUG_INFO_SIZE - used); } else { - used += Strncat(buf + used, (char*)((uint8_t*)gbb + gbb->hwid_offset), - DEBUG_INFO_SIZE - used); - } - - /* Add recovery reason */ - used += Strncat(buf + used, "\nrecovery_reason: 0x", DEBUG_INFO_SIZE - used); - used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, - shared->recovery_reason, 16, 2); - used += Strncat(buf + used, " ", DEBUG_INFO_SIZE - used); - used += Strncat(buf + used, RecoveryReasonString(shared->recovery_reason), - DEBUG_INFO_SIZE - used); - - - /* Add VbSharedData flags */ - used += Strncat(buf + used, "\nVbSD.flags: 0x", DEBUG_INFO_SIZE - used); - used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, - shared->flags, 16, 8); - - /* Add raw contents of VbNvStorage */ - used += Strncat(buf + used, "\nVbNv.raw:", DEBUG_INFO_SIZE - used); - for (i = 0; i < VBNV_BLOCK_SIZE; i++) { - used += Strncat(buf + used, " ", DEBUG_INFO_SIZE - used); - used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, - vncptr->raw[i], 16, 2); + used += Strncat(buf + used, + (char *)((uint8_t *)gbb + gbb->hwid_offset), + DEBUG_INFO_SIZE - used); } - /* Add dev_boot_usb flag */ - VbNvGet(vncptr, VBNV_DEV_BOOT_USB, &i); - used += Strncat(buf + used, "\ndev_boot_usb: ", DEBUG_INFO_SIZE - used); - used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0); - - /* Add dev_boot_legacy flag */ - VbNvGet(vncptr, VBNV_DEV_BOOT_LEGACY, &i); - used += Strncat(buf + used, "\ndev_boot_legacy: ", DEBUG_INFO_SIZE - used); - used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0); - - /* Add dev_boot_signed_only flag */ - VbNvGet(vncptr, VBNV_DEV_BOOT_SIGNED_ONLY, &i); - used += Strncat(buf + used, "\ndev_boot_signed_only: ", - DEBUG_INFO_SIZE - used); - used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0); - - /* Add TPM versions */ - used += Strncat(buf + used, "\nTPM: fwver=0x", DEBUG_INFO_SIZE - used); - used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, - shared->fw_version_tpm, 16, 8); - used += Strncat(buf + used, " kernver=0x", DEBUG_INFO_SIZE - used); - used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, - shared->kernel_version_tpm, 16, 8); - - /* Add GBB flags */ - used += Strncat(buf + used, "\ngbb.flags: 0x", DEBUG_INFO_SIZE - used); - if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1) { - used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, - gbb->flags, 16, 8); - } else { - used += Strncat(buf + used, "0 (default)", DEBUG_INFO_SIZE - used); - } - - /* Add sha1sum for Root & Recovery keys */ - FillInSha1Sum(sha1sum, - (VbPublicKey*)((uint8_t*)gbb + gbb->rootkey_offset)); - used += Strncat(buf + used, "\ngbb.rootkey: ", DEBUG_INFO_SIZE - used); - used += Strncat(buf + used, sha1sum, DEBUG_INFO_SIZE - used); - FillInSha1Sum(sha1sum, - (VbPublicKey*)((uint8_t*)gbb + gbb->recovery_key_offset)); - used += Strncat(buf + used, "\ngbb.recovery_key: ", DEBUG_INFO_SIZE - used); - used += Strncat(buf + used, sha1sum, DEBUG_INFO_SIZE - used); - - /* If we're in dev-mode, show the kernel subkey that we expect, too. */ - if (0 == shared->recovery_reason) { - FillInSha1Sum(sha1sum, &shared->kernel_subkey); - used += Strncat(buf + used, "\nkernel_subkey: ", DEBUG_INFO_SIZE - used); - used += Strncat(buf + used, sha1sum, DEBUG_INFO_SIZE - used); - } - - /* Make sure we finish with a newline */ - used += Strncat(buf + used, "\n", DEBUG_INFO_SIZE - used); - - /* TODO: add more interesting data: - * - Information on current disks */ - - buf[DEBUG_INFO_SIZE - 1] = '\0'; - return VbExDisplayDebugInfo(buf); + /* Add recovery reason */ + used += Strncat(buf + used, + "\nrecovery_reason: 0x", DEBUG_INFO_SIZE - used); + used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, + shared->recovery_reason, 16, 2); + used += Strncat(buf + used, " ", DEBUG_INFO_SIZE - used); + used += Strncat(buf + used, + RecoveryReasonString(shared->recovery_reason), + DEBUG_INFO_SIZE - used); + + /* Add VbSharedData flags */ + used += Strncat(buf + used, "\nVbSD.flags: 0x", DEBUG_INFO_SIZE - used); + used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, + shared->flags, 16, 8); + + /* Add raw contents of VbNvStorage */ + used += Strncat(buf + used, "\nVbNv.raw:", DEBUG_INFO_SIZE - used); + for (i = 0; i < VBNV_BLOCK_SIZE; i++) { + used += Strncat(buf + used, " ", DEBUG_INFO_SIZE - used); + used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, + vncptr->raw[i], 16, 2); + } + + /* Add dev_boot_usb flag */ + VbNvGet(vncptr, VBNV_DEV_BOOT_USB, &i); + used += Strncat(buf + used, "\ndev_boot_usb: ", DEBUG_INFO_SIZE - used); + used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0); + + /* Add dev_boot_legacy flag */ + VbNvGet(vncptr, VBNV_DEV_BOOT_LEGACY, &i); + used += Strncat(buf + used, + "\ndev_boot_legacy: ", DEBUG_INFO_SIZE - used); + used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0); + + /* Add dev_boot_signed_only flag */ + VbNvGet(vncptr, VBNV_DEV_BOOT_SIGNED_ONLY, &i); + used += Strncat(buf + used, "\ndev_boot_signed_only: ", + DEBUG_INFO_SIZE - used); + used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0); + + /* Add TPM versions */ + used += Strncat(buf + used, "\nTPM: fwver=0x", DEBUG_INFO_SIZE - used); + used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, + shared->fw_version_tpm, 16, 8); + used += Strncat(buf + used, " kernver=0x", DEBUG_INFO_SIZE - used); + used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, + shared->kernel_version_tpm, 16, 8); + + /* Add GBB flags */ + used += Strncat(buf + used, "\ngbb.flags: 0x", DEBUG_INFO_SIZE - used); + if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1) { + used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, + gbb->flags, 16, 8); + } else { + used += Strncat(buf + used, + "0 (default)", DEBUG_INFO_SIZE - used); + } + + /* Add sha1sum for Root & Recovery keys */ + FillInSha1Sum(sha1sum, + (VbPublicKey *)((uint8_t *)gbb + gbb->rootkey_offset)); + used += Strncat(buf + used, "\ngbb.rootkey: ", DEBUG_INFO_SIZE - used); + used += Strncat(buf + used, sha1sum, DEBUG_INFO_SIZE - used); + FillInSha1Sum(sha1sum, + (VbPublicKey *)((uint8_t *)gbb + gbb->recovery_key_offset)); + used += Strncat(buf + used, + "\ngbb.recovery_key: ", DEBUG_INFO_SIZE - used); + used += Strncat(buf + used, sha1sum, DEBUG_INFO_SIZE - used); + + /* If we're in dev-mode, show the kernel subkey that we expect, too. */ + if (0 == shared->recovery_reason) { + FillInSha1Sum(sha1sum, &shared->kernel_subkey); + used += Strncat(buf + used, + "\nkernel_subkey: ", DEBUG_INFO_SIZE - used); + used += Strncat(buf + used, sha1sum, DEBUG_INFO_SIZE - used); + } + + /* Make sure we finish with a newline */ + used += Strncat(buf + used, "\n", DEBUG_INFO_SIZE - used); + + /* TODO: add more interesting data: + * - Information on current disks */ + + buf[DEBUG_INFO_SIZE - 1] = '\0'; + return VbExDisplayDebugInfo(buf); } - #define MAGIC_WORD_LEN 5 -#define MAGIC_WORD "xyzzy" +#define MAGIC_WORD "xyzzy" static uint8_t MagicBuffer[MAGIC_WORD_LEN]; -VbError_t VbCheckDisplayKey(VbCommonParams* cparams, uint32_t key, - VbNvContext *vncptr) { - int i; - - /* Update key buffer */ - for(i=1; i<MAGIC_WORD_LEN; i++) - MagicBuffer[i-1] = MagicBuffer[i]; - /* Save as lower-case ASCII */ - MagicBuffer[MAGIC_WORD_LEN-1] = (key | 0x20) & 0xFF; - - if ('\t' == key) { - /* Tab = display debug info */ - return VbDisplayDebugInfo(cparams, vncptr); - } else if (VB_KEY_LEFT == key || VB_KEY_RIGHT == key || - VB_KEY_DOWN == key || VB_KEY_UP == key) { - /* Arrow keys = change localization */ - uint32_t loc = 0; - uint32_t count = 0; - - VbNvGet(vncptr, VBNV_LOCALIZATION_INDEX, &loc); - if (VBERROR_SUCCESS != VbGetLocalizationCount(cparams, &count)) - loc = 0; /* No localization count (bad GBB?), so set to 0 (default) */ - else if (VB_KEY_RIGHT == key || VB_KEY_UP == key) - loc = (loc < count - 1 ? loc + 1 : 0); - else - loc = (loc > 0 ? loc - 1 : count - 1); - VBDEBUG(("VbCheckDisplayKey() - change localization to %d\n", (int)loc)); - VbNvSet(vncptr, VBNV_LOCALIZATION_INDEX, loc); +VbError_t VbCheckDisplayKey(VbCommonParams *cparams, uint32_t key, + VbNvContext *vncptr) +{ + int i; + + /* Update key buffer */ + for(i = 1; i < MAGIC_WORD_LEN; i++) + MagicBuffer[i - 1] = MagicBuffer[i]; + /* Save as lower-case ASCII */ + MagicBuffer[MAGIC_WORD_LEN - 1] = (key | 0x20) & 0xFF; + + if ('\t' == key) { + /* Tab = display debug info */ + return VbDisplayDebugInfo(cparams, vncptr); + } else if (VB_KEY_LEFT == key || VB_KEY_RIGHT == key || + VB_KEY_DOWN == key || VB_KEY_UP == key) { + /* Arrow keys = change localization */ + uint32_t loc = 0; + uint32_t count = 0; + + VbNvGet(vncptr, VBNV_LOCALIZATION_INDEX, &loc); + if (VBERROR_SUCCESS != VbGetLocalizationCount(cparams, &count)) + loc = 0; /* No localization count (bad GBB?) */ + else if (VB_KEY_RIGHT == key || VB_KEY_UP == key) + loc = (loc < count - 1 ? loc + 1 : 0); + else + loc = (loc > 0 ? loc - 1 : count - 1); + VBDEBUG(("VbCheckDisplayKey() - change localization to %d\n", + (int)loc)); + VbNvSet(vncptr, VBNV_LOCALIZATION_INDEX, loc); #ifdef SAVE_LOCALE_IMMEDIATELY - VbNvTeardown(vncptr); /* really only computes checksum */ - if (vncptr->raw_changed) - VbExNvStorageWrite(vncptr->raw); + VbNvTeardown(vncptr); /* really only computes checksum */ + if (vncptr->raw_changed) + VbExNvStorageWrite(vncptr->raw); #endif - /* Force redraw of current screen */ - return VbDisplayScreen(cparams, disp_current_screen, 1, vncptr); - } + /* Force redraw of current screen */ + return VbDisplayScreen(cparams, disp_current_screen, 1, vncptr); + } - if (0 == Memcmp(MagicBuffer, MAGIC_WORD, MAGIC_WORD_LEN)) { - if (VBEASTEREGG) - (void)VbDisplayScreen(cparams, disp_current_screen, 1, vncptr); - } + if (0 == Memcmp(MagicBuffer, MAGIC_WORD, MAGIC_WORD_LEN)) { + if (VBEASTEREGG) + (void)VbDisplayScreen(cparams, disp_current_screen, + 1, vncptr); + } return VBERROR_SUCCESS; } diff --git a/firmware/lib/vboot_firmware.c b/firmware/lib/vboot_firmware.c index ed9d3bc0..c2e8293f 100644 --- a/firmware/lib/vboot_firmware.c +++ b/firmware/lib/vboot_firmware.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. * @@ -13,311 +13,350 @@ #include "vboot_common.h" #include "vboot_nvstorage.h" -/* Static variables for UpdateFirmwareBodyHash(). It's less than - * optimal to have static variables in a library, but in UEFI the - * caller is deep inside a different firmware stack and doesn't have a - * good way to pass the params struct back to us. */ +/* + * Static variables for UpdateFirmwareBodyHash(). It's less than optimal to + * have static variables in a library, but in UEFI the caller is deep inside a + * different firmware stack and doesn't have a good way to pass the params + * struct back to us. + */ typedef struct VbLoadFirmwareInternal { - DigestContext body_digest_context; - uint32_t body_size_accum; + DigestContext body_digest_context; + uint32_t body_size_accum; } VbLoadFirmwareInternal; +void VbUpdateFirmwareBodyHash(VbCommonParams *cparams, uint8_t *data, + uint32_t size) +{ + VbLoadFirmwareInternal *lfi = + (VbLoadFirmwareInternal*)cparams->vboot_context; -void VbUpdateFirmwareBodyHash(VbCommonParams* cparams, - uint8_t* data, uint32_t size) { - VbLoadFirmwareInternal* lfi = - (VbLoadFirmwareInternal*)cparams->vboot_context; - - DigestUpdate(&lfi->body_digest_context, data, size); - lfi->body_size_accum += size; + DigestUpdate(&lfi->body_digest_context, data, size); + lfi->body_size_accum += size; } - -int LoadFirmware(VbCommonParams* cparams, VbSelectFirmwareParams* fparams, - VbNvContext* vnc) { - VbSharedDataHeader* shared = (VbSharedDataHeader*)cparams->shared_data_blob; - GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)cparams->gbb_data; - VbPublicKey* root_key; - VbLoadFirmwareInternal* lfi; - - uint32_t try_b_count; - uint32_t lowest_version = 0xFFFFFFFF; - int good_index = -1; - int is_dev; - int index; - int i; - - int retval = VBERROR_UNKNOWN; - int recovery = VBNV_RECOVERY_RO_UNSPECIFIED; - - /* Clear output params in case we fail */ - shared->firmware_index = 0xFF; - - VBDEBUG(("LoadFirmware started...\n")); - - /* Must have a root key from the GBB */ - if (!gbb) { - VBDEBUG(("No GBB\n")); - retval = VBERROR_INVALID_GBB; - goto LoadFirmwareExit; - } - root_key = (VbPublicKey*)((uint8_t*)gbb + gbb->rootkey_offset); - - /* Parse flags */ - is_dev = (shared->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0); - if (is_dev) - shared->flags |= VBSD_LF_DEV_SWITCH_ON; - - /* Read try-b count and decrement if necessary */ - VbNvGet(vnc, VBNV_TRY_B_COUNT, &try_b_count); - if (0 != try_b_count) { - VbNvSet(vnc, VBNV_TRY_B_COUNT, try_b_count - 1); - shared->flags |= VBSD_FWB_TRIED; - } - - /* Allocate our internal data */ - lfi = (VbLoadFirmwareInternal*)VbExMalloc(sizeof(VbLoadFirmwareInternal)); - cparams->vboot_context = (void*)lfi; - - /* Loop over indices */ - for (i = 0; i < 2; i++) { - VbKeyBlockHeader* key_block; - uint32_t vblock_size; - VbFirmwarePreambleHeader* preamble; - RSAPublicKey* data_key; - uint64_t key_version; - uint32_t combined_version; - uint8_t* body_digest; - uint8_t* check_result; - - /* If try B count is non-zero try firmware B first */ - index = (try_b_count ? 1 - i : i); - if (0 == index) { - key_block = (VbKeyBlockHeader*)fparams->verification_block_A; - vblock_size = fparams->verification_size_A; - check_result = &shared->check_fw_a_result; - } else { - key_block = (VbKeyBlockHeader*)fparams->verification_block_B; - vblock_size = fparams->verification_size_B; - check_result = &shared->check_fw_b_result; - } - - /* Check the key block flags against the current boot mode. Do this - * before verifying the key block, since flags are faster to check than - * the RSA signature. */ - if (!(key_block->key_block_flags & - (is_dev ? KEY_BLOCK_FLAG_DEVELOPER_1 : - KEY_BLOCK_FLAG_DEVELOPER_0))) { - VBDEBUG(("Developer flag mismatch.\n")); - *check_result = VBSD_LF_CHECK_DEV_MISMATCH; - continue; - } - /* RW firmware never runs in recovery mode. */ - if (!(key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0)) { - VBDEBUG(("Recovery flag mismatch.\n")); - *check_result = VBSD_LF_CHECK_REC_MISMATCH; - continue; - } - - /* Verify the key block */ - VBPERFSTART("VB_VKB"); - if ((0 != KeyBlockVerify(key_block, vblock_size, root_key, 0))) { - VBDEBUG(("Key block verification failed.\n")); - *check_result = VBSD_LF_CHECK_VERIFY_KEYBLOCK; - VBPERFEND("VB_VKB"); - continue; - } - VBPERFEND("VB_VKB"); - - /* Check for rollback of key version. */ - key_version = key_block->data_key.key_version; - if (!(gbb->flags & GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK)) { - if (key_version < (shared->fw_version_tpm >> 16)) { - VBDEBUG(("Key rollback detected.\n")); - *check_result = VBSD_LF_CHECK_KEY_ROLLBACK; - continue; - } - if (key_version > 0xFFFF) { - /* Key version is stored in 16 bits in the TPM, so key versions greater - * than 0xFFFF can't be stored properly. */ - VBDEBUG(("Key version > 0xFFFF.\n")); - *check_result = VBSD_LF_CHECK_KEY_ROLLBACK; - continue; - } - } - - /* Get the key for preamble/data verification from the key block. */ - data_key = PublicKeyToRSA(&key_block->data_key); - if (!data_key) { - VBDEBUG(("Unable to parse data key.\n")); - *check_result = VBSD_LF_CHECK_DATA_KEY_PARSE; - continue; - } - - /* Verify the preamble, which follows the key block. */ - VBPERFSTART("VB_VPB"); - preamble = (VbFirmwarePreambleHeader*)((uint8_t*)key_block + - key_block->key_block_size); - if ((0 != VerifyFirmwarePreamble(preamble, - vblock_size - key_block->key_block_size, - data_key))) { - VBDEBUG(("Preamble verfication failed.\n")); - *check_result = VBSD_LF_CHECK_VERIFY_PREAMBLE; - RSAPublicKeyFree(data_key); - VBPERFEND("VB_VPB"); - continue; - } - VBPERFEND("VB_VPB"); - - /* Check for rollback of firmware version. */ - combined_version = (uint32_t)((key_version << 16) | - (preamble->firmware_version & 0xFFFF)); - if (combined_version < shared->fw_version_tpm && - !(gbb->flags & GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK)) { - VBDEBUG(("Firmware version rollback detected.\n")); - *check_result = VBSD_LF_CHECK_FW_ROLLBACK; - RSAPublicKeyFree(data_key); - continue; - } - - /* Header for this firmware is valid */ - *check_result = VBSD_LF_CHECK_HEADER_VALID; - - /* Check for lowest key version from a valid header. */ - if (lowest_version > combined_version) - lowest_version = combined_version; - - /* If we already have good firmware, no need to read another one; - * we only needed to look at the versions to check for - * rollback. */ - if (-1 != good_index) { - RSAPublicKeyFree(data_key); - continue; - } - - /* Handle preamble flag for using the RO normal/dev code path */ - if (VbGetFirmwarePreambleFlags(preamble) & - VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL) { - - /* Fail if calling firmware doesn't support RO normal */ - if (!(shared->flags & VBSD_BOOT_RO_NORMAL_SUPPORT)) { - *check_result = VBSD_LF_CHECK_NO_RO_NORMAL; - RSAPublicKeyFree(data_key); - continue; - } - - /* Indicate that we should use the RO normal code path */ - shared->flags |= VBSD_LF_USE_RO_NORMAL; - - } else { - VbError_t rv; - - /* Read the firmware data */ - VBPERFSTART("VB_RFD"); - DigestInit(&lfi->body_digest_context, data_key->algorithm); - lfi->body_size_accum = 0; - rv = VbExHashFirmwareBody(cparams, (index ? VB_SELECT_FIRMWARE_B : - VB_SELECT_FIRMWARE_A)); - if (VBERROR_SUCCESS != rv) { - VBDEBUG(("VbExHashFirmwareBody() failed for index %d\n", index)); - *check_result = VBSD_LF_CHECK_GET_FW_BODY; - RSAPublicKeyFree(data_key); - VBPERFEND("VB_RFD"); - continue; - } - if (lfi->body_size_accum != preamble->body_signature.data_size) { - VBDEBUG(("Hash updated %d bytes but expected %d\n", - (int)lfi->body_size_accum, - (int)preamble->body_signature.data_size)); - *check_result = VBSD_LF_CHECK_HASH_WRONG_SIZE; - RSAPublicKeyFree(data_key); - VBPERFEND("VB_RFD"); - continue; - } - VBPERFEND("VB_RFD"); - - /* Verify firmware data */ - VBPERFSTART("VB_VFD"); - body_digest = DigestFinal(&lfi->body_digest_context); - if (0 != VerifyDigest(body_digest, &preamble->body_signature, - data_key)) { - VBDEBUG(("Firmware body verification failed.\n")); - *check_result = VBSD_LF_CHECK_VERIFY_BODY; - RSAPublicKeyFree(data_key); - VbExFree(body_digest); - VBPERFEND("VB_VFD"); - continue; - } - VbExFree(body_digest); - VBPERFEND("VB_VFD"); - } - - /* Done with the data key, so can free it now */ - RSAPublicKeyFree(data_key); - - /* If we're still here, the firmware is valid. */ - VBDEBUG(("Firmware %d is valid.\n", index)); - *check_result = VBSD_LF_CHECK_VALID; - if (-1 == good_index) { - /* Save the key we actually used */ - if (0 != VbSharedDataSetKernelKey(shared, &preamble->kernel_subkey)) { - VBDEBUG(("Unable to save kernel subkey to shared data.\n")); - continue; /* The firmware signature was good, but the public - * key was bigger that the caller can handle. */ - } - - /* Save the good index, now that we're sure we can actually use - * this firmware. That's the one we'll boot. */ - good_index = index; - shared->firmware_index = (uint8_t)index; - shared->fw_keyblock_flags = key_block->key_block_flags; - - /* If the good firmware's key version is the same as the tpm, - * then the TPM doesn't need updating; we can stop now. - * Otherwise, we'll check all the other headers to see if they - * contain a newer key. */ - if (combined_version == shared->fw_version_tpm) - break; - } - } - - /* Free internal data */ - VbExFree(lfi); - cparams->vboot_context = NULL; - - /* Handle finding good firmware */ - if (good_index >= 0) { - - /* Save versions we found */ - shared->fw_version_lowest = lowest_version; - if (lowest_version > shared->fw_version_tpm) - shared->fw_version_tpm = lowest_version; - - /* Success */ - VBDEBUG(("Will boot firmware index %d\n", (int)shared->firmware_index)); - retval = VBERROR_SUCCESS; - } else { - uint8_t a = shared->check_fw_a_result; - uint8_t b = shared->check_fw_b_result; - uint8_t best_check; - - /* No good firmware, so go to recovery mode. */ - VBDEBUG(("Alas, no good firmware.\n")); - recovery = VBNV_RECOVERY_RO_INVALID_RW; - retval = VBERROR_LOAD_FIRMWARE; - - /* If the best check result fits in the range of recovery reasons, provide - * more detail on how far we got in validation. */ - best_check = (a > b ? a : b) + VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN; - if (best_check >= VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN && - best_check <= VBNV_RECOVERY_RO_INVALID_RW_CHECK_MAX) - recovery = best_check; - } - -LoadFirmwareExit: - /* Store recovery request, if any */ - VbNvSet(vnc, VBNV_RECOVERY_REQUEST, VBERROR_SUCCESS != retval ? - recovery : VBNV_RECOVERY_NOT_REQUESTED); - - return retval; +int LoadFirmware(VbCommonParams *cparams, VbSelectFirmwareParams *fparams, + VbNvContext *vnc) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)cparams->shared_data_blob; + GoogleBinaryBlockHeader *gbb = + (GoogleBinaryBlockHeader *)cparams->gbb_data; + VbPublicKey *root_key; + VbLoadFirmwareInternal *lfi; + + uint32_t try_b_count; + uint32_t lowest_version = 0xFFFFFFFF; + int good_index = -1; + int is_dev; + int index; + int i; + + int retval = VBERROR_UNKNOWN; + int recovery = VBNV_RECOVERY_RO_UNSPECIFIED; + + /* Clear output params in case we fail */ + shared->firmware_index = 0xFF; + + VBDEBUG(("LoadFirmware started...\n")); + + /* Must have a root key from the GBB */ + if (!gbb) { + VBDEBUG(("No GBB\n")); + retval = VBERROR_INVALID_GBB; + goto LoadFirmwareExit; + } + root_key = (VbPublicKey *)((uint8_t *)gbb + gbb->rootkey_offset); + + /* Parse flags */ + is_dev = (shared->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0); + if (is_dev) + shared->flags |= VBSD_LF_DEV_SWITCH_ON; + + /* Read try-b count and decrement if necessary */ + VbNvGet(vnc, VBNV_TRY_B_COUNT, &try_b_count); + if (0 != try_b_count) { + VbNvSet(vnc, VBNV_TRY_B_COUNT, try_b_count - 1); + shared->flags |= VBSD_FWB_TRIED; + } + + /* Allocate our internal data */ + lfi = (VbLoadFirmwareInternal *) + VbExMalloc(sizeof(VbLoadFirmwareInternal)); + cparams->vboot_context = lfi; + + /* Loop over indices */ + for (i = 0; i < 2; i++) { + VbKeyBlockHeader *key_block; + uint32_t vblock_size; + VbFirmwarePreambleHeader *preamble; + RSAPublicKey *data_key; + uint64_t key_version; + uint32_t combined_version; + uint8_t *body_digest; + uint8_t *check_result; + + /* If try B count is non-zero try firmware B first */ + index = (try_b_count ? 1 - i : i); + if (0 == index) { + key_block = (VbKeyBlockHeader *) + fparams->verification_block_A; + vblock_size = fparams->verification_size_A; + check_result = &shared->check_fw_a_result; + } else { + key_block = (VbKeyBlockHeader *) + fparams->verification_block_B; + vblock_size = fparams->verification_size_B; + check_result = &shared->check_fw_b_result; + } + + /* + * Check the key block flags against the current boot mode. Do + * this before verifying the key block, since flags are faster + * to check than the RSA signature. + */ + if (!(key_block->key_block_flags & + (is_dev ? KEY_BLOCK_FLAG_DEVELOPER_1 : + KEY_BLOCK_FLAG_DEVELOPER_0))) { + VBDEBUG(("Developer flag mismatch.\n")); + *check_result = VBSD_LF_CHECK_DEV_MISMATCH; + continue; + } + + /* RW firmware never runs in recovery mode. */ + if (!(key_block->key_block_flags & KEY_BLOCK_FLAG_RECOVERY_0)) { + VBDEBUG(("Recovery flag mismatch.\n")); + *check_result = VBSD_LF_CHECK_REC_MISMATCH; + continue; + } + + /* Verify the key block */ + VBPERFSTART("VB_VKB"); + if ((0 != KeyBlockVerify(key_block, vblock_size, + root_key, 0))) { + VBDEBUG(("Key block verification failed.\n")); + *check_result = VBSD_LF_CHECK_VERIFY_KEYBLOCK; + VBPERFEND("VB_VKB"); + continue; + } + VBPERFEND("VB_VKB"); + + /* Check for rollback of key version. */ + key_version = key_block->data_key.key_version; + if (!(gbb->flags & GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK)) { + if (key_version < (shared->fw_version_tpm >> 16)) { + VBDEBUG(("Key rollback detected.\n")); + *check_result = VBSD_LF_CHECK_KEY_ROLLBACK; + continue; + } + if (key_version > 0xFFFF) { + /* + * Key version is stored in 16 bits in the TPM, + * so key versions greater than 0xFFFF can't be + * stored properly. + */ + VBDEBUG(("Key version > 0xFFFF.\n")); + *check_result = VBSD_LF_CHECK_KEY_ROLLBACK; + continue; + } + } + + /* Get key for preamble/data verification from the key block. */ + data_key = PublicKeyToRSA(&key_block->data_key); + if (!data_key) { + VBDEBUG(("Unable to parse data key.\n")); + *check_result = VBSD_LF_CHECK_DATA_KEY_PARSE; + continue; + } + + /* Verify the preamble, which follows the key block. */ + VBPERFSTART("VB_VPB"); + preamble = (VbFirmwarePreambleHeader *) + ((uint8_t *)key_block + key_block->key_block_size); + if ((0 != VerifyFirmwarePreamble( + preamble, + vblock_size - key_block->key_block_size, + data_key))) { + VBDEBUG(("Preamble verfication failed.\n")); + *check_result = VBSD_LF_CHECK_VERIFY_PREAMBLE; + RSAPublicKeyFree(data_key); + VBPERFEND("VB_VPB"); + continue; + } + VBPERFEND("VB_VPB"); + + /* Check for rollback of firmware version. */ + combined_version = (uint32_t)((key_version << 16) | + (preamble->firmware_version & 0xFFFF)); + if (combined_version < shared->fw_version_tpm && + !(gbb->flags & GBB_FLAG_DISABLE_FW_ROLLBACK_CHECK)) { + VBDEBUG(("Firmware version rollback detected.\n")); + *check_result = VBSD_LF_CHECK_FW_ROLLBACK; + RSAPublicKeyFree(data_key); + continue; + } + + /* Header for this firmware is valid */ + *check_result = VBSD_LF_CHECK_HEADER_VALID; + + /* Check for lowest key version from a valid header. */ + if (lowest_version > combined_version) + lowest_version = combined_version; + + /* + * If we already have good firmware, no need to read another + * one; we only needed to look at the versions to check for + * rollback. + */ + if (-1 != good_index) { + RSAPublicKeyFree(data_key); + continue; + } + + /* Handle preamble flag for using the RO normal/dev code path */ + if (VbGetFirmwarePreambleFlags(preamble) & + VB_FIRMWARE_PREAMBLE_USE_RO_NORMAL) { + + /* Fail if calling firmware doesn't support RO normal */ + if (!(shared->flags & VBSD_BOOT_RO_NORMAL_SUPPORT)) { + *check_result = VBSD_LF_CHECK_NO_RO_NORMAL; + RSAPublicKeyFree(data_key); + continue; + } + + /* Use the RO normal code path */ + shared->flags |= VBSD_LF_USE_RO_NORMAL; + + } else { + VbError_t rv; + + /* Read the firmware data */ + VBPERFSTART("VB_RFD"); + DigestInit(&lfi->body_digest_context, + data_key->algorithm); + lfi->body_size_accum = 0; + rv = VbExHashFirmwareBody( + cparams, + (index ? VB_SELECT_FIRMWARE_B : + VB_SELECT_FIRMWARE_A)); + if (VBERROR_SUCCESS != rv) { + VBDEBUG(("VbExHashFirmwareBody() failed for " + "index %d\n", index)); + *check_result = VBSD_LF_CHECK_GET_FW_BODY; + RSAPublicKeyFree(data_key); + VBPERFEND("VB_RFD"); + continue; + } + if (lfi->body_size_accum != + preamble->body_signature.data_size) { + VBDEBUG(("Hashed %d bytes but expected %d\n", + (int)lfi->body_size_accum, + (int)preamble->body_signature.data_size)); + *check_result = VBSD_LF_CHECK_HASH_WRONG_SIZE; + RSAPublicKeyFree(data_key); + VBPERFEND("VB_RFD"); + continue; + } + VBPERFEND("VB_RFD"); + + /* Verify firmware data */ + VBPERFSTART("VB_VFD"); + body_digest = DigestFinal(&lfi->body_digest_context); + if (0 != VerifyDigest(body_digest, + &preamble->body_signature, + data_key)) { + VBDEBUG(("FW body verification failed.\n")); + *check_result = VBSD_LF_CHECK_VERIFY_BODY; + RSAPublicKeyFree(data_key); + VbExFree(body_digest); + VBPERFEND("VB_VFD"); + continue; + } + VbExFree(body_digest); + VBPERFEND("VB_VFD"); + } + + /* Done with the data key, so can free it now */ + RSAPublicKeyFree(data_key); + + /* If we're still here, the firmware is valid. */ + VBDEBUG(("Firmware %d is valid.\n", index)); + *check_result = VBSD_LF_CHECK_VALID; + if (-1 == good_index) { + /* Save the key we actually used */ + if (0 != VbSharedDataSetKernelKey( + shared, &preamble->kernel_subkey)) { + /* + * The firmware signature was good, but the + * public key was bigger that the caller can + * handle. + */ + VBDEBUG(("Unable to save kernel subkey.\n")); + continue; + } + + /* + * Save the good index, now that we're sure we can + * actually use this firmware. That's the one we'll + * boot. + */ + good_index = index; + shared->firmware_index = (uint8_t)index; + shared->fw_keyblock_flags = key_block->key_block_flags; + + /* + * If the good firmware's key version is the same as + * the tpm, then the TPM doesn't need updating; we can + * stop now. Otherwise, we'll check all the other + * headers to see if they contain a newer key. + */ + if (combined_version == shared->fw_version_tpm) + break; + } + } + + /* Free internal data */ + VbExFree(lfi); + cparams->vboot_context = NULL; + + /* Handle finding good firmware */ + if (good_index >= 0) { + + /* Save versions we found */ + shared->fw_version_lowest = lowest_version; + if (lowest_version > shared->fw_version_tpm) + shared->fw_version_tpm = lowest_version; + + /* Success */ + VBDEBUG(("Will boot firmware index %d\n", + (int)shared->firmware_index)); + retval = VBERROR_SUCCESS; + + } else { + uint8_t a = shared->check_fw_a_result; + uint8_t b = shared->check_fw_b_result; + uint8_t best_check; + + /* No good firmware, so go to recovery mode. */ + VBDEBUG(("Alas, no good firmware.\n")); + recovery = VBNV_RECOVERY_RO_INVALID_RW; + retval = VBERROR_LOAD_FIRMWARE; + + /* + * If the best check result fits in the range of recovery + * reasons, provide more detail on how far we got in + * validation. + */ + best_check = (a > b ? a : b) + + VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN; + if (best_check >= VBNV_RECOVERY_RO_INVALID_RW_CHECK_MIN && + best_check <= VBNV_RECOVERY_RO_INVALID_RW_CHECK_MAX) + recovery = best_check; + } + + LoadFirmwareExit: + /* Store recovery request, if any */ + VbNvSet(vnc, VBNV_RECOVERY_REQUEST, VBERROR_SUCCESS != retval ? + recovery : VBNV_RECOVERY_NOT_REQUESTED); + + return retval; } diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c index ec77de36..26eba7e7 100644 --- a/firmware/lib/vboot_kernel.c +++ b/firmware/lib/vboot_kernel.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. * @@ -20,552 +20,621 @@ #define LOWEST_TPM_VERSION 0xffffffff typedef enum BootMode { - kBootRecovery = 0, /* Recovery firmware, regardless of dev switch position */ - kBootNormal = 1, /* Normal boot - kernel must be verified */ - kBootDev = 2 /* Developer boot - self-signed kernel ok */ + kBootRecovery = 0, /* Recovery firmware, any dev switch position */ + kBootNormal = 1, /* Normal boot - kernel must be verified */ + kBootDev = 2 /* Developer boot - self-signed kernel ok */ } BootMode; - -/* Allocates and reads GPT data from the drive. The sector_bytes and - * drive_sectors fields should be filled on input. The primary and - * secondary header and entries are filled on output. +/** + * Allocate and read GPT data from the drive. + * + * The sector_bytes and drive_sectors fields should be filled on input. The + * primary and secondary header and entries are filled on output. * - * Returns 0 if successful, 1 if error. */ -int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData* gptdata) { - - uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes; - - /* No data to be written yet */ - gptdata->modified = 0; - - /* Allocate all buffers */ - gptdata->primary_header = (uint8_t*)VbExMalloc(gptdata->sector_bytes); - gptdata->secondary_header = (uint8_t*)VbExMalloc(gptdata->sector_bytes); - gptdata->primary_entries = (uint8_t*)VbExMalloc(TOTAL_ENTRIES_SIZE); - gptdata->secondary_entries = (uint8_t*)VbExMalloc(TOTAL_ENTRIES_SIZE); - - if (gptdata->primary_header == NULL || gptdata->secondary_header == NULL || - gptdata->primary_entries == NULL || gptdata->secondary_entries == NULL) - return 1; - - /* Read data from the drive, skipping the protective MBR */ - if (0 != VbExDiskRead(disk_handle, 1, 1, gptdata->primary_header)) - return 1; - if (0 != VbExDiskRead(disk_handle, 2, entries_sectors, - gptdata->primary_entries)) - return 1; - if (0 != VbExDiskRead(disk_handle, - gptdata->drive_sectors - entries_sectors - 1, - entries_sectors, gptdata->secondary_entries)) - return 1; - if (0 != VbExDiskRead(disk_handle, gptdata->drive_sectors - 1, 1, - gptdata->secondary_header)) - return 1; - - return 0; + * Returns 0 if successful, 1 if error. + */ +int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) +{ + uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes; + + /* No data to be written yet */ + gptdata->modified = 0; + + /* Allocate all buffers */ + gptdata->primary_header = (uint8_t *)VbExMalloc(gptdata->sector_bytes); + gptdata->secondary_header = + (uint8_t *)VbExMalloc(gptdata->sector_bytes); + gptdata->primary_entries = (uint8_t *)VbExMalloc(TOTAL_ENTRIES_SIZE); + gptdata->secondary_entries = (uint8_t *)VbExMalloc(TOTAL_ENTRIES_SIZE); + + if (gptdata->primary_header == NULL || + gptdata->secondary_header == NULL || + gptdata->primary_entries == NULL || + gptdata->secondary_entries == NULL) + return 1; + + /* Read data from the drive, skipping the protective MBR */ + if (0 != VbExDiskRead(disk_handle, 1, 1, gptdata->primary_header)) + return 1; + if (0 != VbExDiskRead(disk_handle, 2, entries_sectors, + gptdata->primary_entries)) + return 1; + if (0 != VbExDiskRead(disk_handle, + gptdata->drive_sectors - entries_sectors - 1, + entries_sectors, gptdata->secondary_entries)) + return 1; + if (0 != VbExDiskRead(disk_handle, gptdata->drive_sectors - 1, 1, + gptdata->secondary_header)) + return 1; + + return 0; } - -/* Writes any changes for the GPT data back to the drive, then frees - * the buffers. +/** + * Write any changes for the GPT data back to the drive, then free the buffers. * - * Returns 0 if successful, 1 if error. */ -int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData* gptdata) { - - int legacy = 0; - uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes; - - if (gptdata->primary_header) { - GptHeader* h = (GptHeader*)(gptdata->primary_header); - legacy = !Memcmp(h->signature, GPT_HEADER_SIGNATURE2, - GPT_HEADER_SIGNATURE_SIZE); - if (gptdata->modified & GPT_MODIFIED_HEADER1) { - if (legacy) { - VBDEBUG(("Not updating GPT header 1: legacy mode is enabled.\n")); - } else { - VBDEBUG(("Updating GPT header 1\n")); - if (0 != VbExDiskWrite(disk_handle, 1, 1, gptdata->primary_header)) - return 1; - } - } - VbExFree(gptdata->primary_header); - } - - if (gptdata->primary_entries) { - if (gptdata->modified & GPT_MODIFIED_ENTRIES1) { - if (legacy) { - VBDEBUG(("Not updating GPT entries 1: legacy mode is enabled.\n")); - } else { - VBDEBUG(("Updating GPT entries 1\n")); - if (0 != VbExDiskWrite(disk_handle, 2, entries_sectors, - gptdata->primary_entries)) - return 1; - } - } - VbExFree(gptdata->primary_entries); - } - - if (gptdata->secondary_entries) { - if (gptdata->modified & GPT_MODIFIED_ENTRIES2) { - VBDEBUG(("Updating GPT header 2\n")); - if (0 != VbExDiskWrite(disk_handle, - gptdata->drive_sectors - entries_sectors - 1, - entries_sectors, gptdata->secondary_entries)) - return 1; - } - VbExFree(gptdata->secondary_entries); - } - - if (gptdata->secondary_header) { - if (gptdata->modified & GPT_MODIFIED_HEADER2) { - VBDEBUG(("Updating GPT entries 2\n")); - if (0 != VbExDiskWrite(disk_handle, gptdata->drive_sectors - 1, 1, - gptdata->secondary_header)) - return 1; - } - VbExFree(gptdata->secondary_header); - } - - /* Success */ - return 0; + * Returns 0 if successful, 1 if error. + */ +int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata) +{ + int legacy = 0; + uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes; + + if (gptdata->primary_header) { + GptHeader *h = (GptHeader *)(gptdata->primary_header); + legacy = !Memcmp(h->signature, GPT_HEADER_SIGNATURE2, + GPT_HEADER_SIGNATURE_SIZE); + if (gptdata->modified & GPT_MODIFIED_HEADER1) { + if (legacy) { + VBDEBUG(("Not updating GPT header 1: " + "legacy mode is enabled.\n")); + } else { + VBDEBUG(("Updating GPT header 1\n")); + if (0 != VbExDiskWrite(disk_handle, 1, 1, + gptdata->primary_header)) + return 1; + } + } + VbExFree(gptdata->primary_header); + } + + if (gptdata->primary_entries) { + if (gptdata->modified & GPT_MODIFIED_ENTRIES1) { + if (legacy) { + VBDEBUG(("Not updating GPT entries 1: " + "legacy mode is enabled.\n")); + } else { + VBDEBUG(("Updating GPT entries 1\n")); + if (0 != VbExDiskWrite(disk_handle, 2, + entries_sectors, + gptdata->primary_entries)) + return 1; + } + } + VbExFree(gptdata->primary_entries); + } + + if (gptdata->secondary_entries) { + if (gptdata->modified & GPT_MODIFIED_ENTRIES2) { + VBDEBUG(("Updating GPT header 2\n")); + if (0 != VbExDiskWrite(disk_handle, + gptdata->drive_sectors - entries_sectors - 1, + entries_sectors, gptdata->secondary_entries)) + return 1; + } + VbExFree(gptdata->secondary_entries); + } + + if (gptdata->secondary_header) { + if (gptdata->modified & GPT_MODIFIED_HEADER2) { + VBDEBUG(("Updating GPT entries 2\n")); + if (0 != VbExDiskWrite(disk_handle, + gptdata->drive_sectors - 1, 1, + gptdata->secondary_header)) + return 1; + } + VbExFree(gptdata->secondary_header); + } + + /* Success */ + return 0; } /* disable MSVC warning on const logical expression (as in } while(0);) */ __pragma(warning(disable: 4127)) - -VbError_t LoadKernel(LoadKernelParams* params) { - VbSharedDataHeader* shared = (VbSharedDataHeader*)params->shared_data_blob; - VbSharedDataKernelCall* shcall = NULL; - VbNvContext* vnc = params->nv_context; - GoogleBinaryBlockHeader* gbb = (GoogleBinaryBlockHeader*)params->gbb_data; - VbPublicKey* kernel_subkey; - GptData gpt; - uint64_t part_start, part_size; - uint64_t blba; - uint64_t kbuf_sectors; - uint8_t* kbuf = NULL; - int found_partitions = 0; - int good_partition = -1; - int good_partition_key_block_valid = 0; - uint32_t lowest_version = LOWEST_TPM_VERSION; - int rec_switch, dev_switch; - BootMode boot_mode; - uint32_t require_official_os = 0; - - VbError_t retval = VBERROR_UNKNOWN; - int recovery = VBNV_RECOVERY_LK_UNSPECIFIED; - - /* Sanity Checks */ - if (!params || - !params->bytes_per_lba || - !params->ending_lba) { - VBDEBUG(("LoadKernel() called with invalid params\n")); - retval = VBERROR_INVALID_PARAMETER; - goto LoadKernelExit; - } - - /* Clear output params in case we fail */ - params->partition_number = 0; - params->bootloader_address = 0; - params->bootloader_size = 0; - - /* Calculate switch positions and boot mode */ - rec_switch = (BOOT_FLAG_RECOVERY & params->boot_flags ? 1 : 0); - dev_switch = (BOOT_FLAG_DEVELOPER & params->boot_flags ? 1 : 0); - if (rec_switch) { - boot_mode = kBootRecovery; - } else if (dev_switch) { - boot_mode = kBootDev; - VbNvGet(vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &require_official_os); - } else { - boot_mode = kBootNormal; - } - - /* Set up tracking for this call. This wraps around if called many times, - * so we need to initialize the call entry each time. */ - shcall = shared->lk_calls + (shared->lk_call_count - & (VBSD_MAX_KERNEL_CALLS - 1)); - Memset(shcall, 0, sizeof(VbSharedDataKernelCall)); - shcall->boot_flags = (uint32_t)params->boot_flags; - shcall->boot_mode = boot_mode; - shcall->sector_size = (uint32_t)params->bytes_per_lba; - shcall->sector_count = params->ending_lba + 1; - shared->lk_call_count++; - - /* Initialization */ - blba = params->bytes_per_lba; - kbuf_sectors = KBUF_SIZE / blba; - if (0 == kbuf_sectors) { - VBDEBUG(("LoadKernel() called with sector size > KBUF_SIZE\n")); - retval = VBERROR_INVALID_PARAMETER; - goto LoadKernelExit; - } - - if (kBootRecovery == boot_mode) { - /* Use the recovery key to verify the kernel */ - kernel_subkey = (VbPublicKey*)((uint8_t*)gbb + gbb->recovery_key_offset); - } else { - /* Use the kernel subkey passed from LoadFirmware(). */ - kernel_subkey = &shared->kernel_subkey; - } - - do { - /* Read GPT data */ - gpt.sector_bytes = (uint32_t)blba; - gpt.drive_sectors = params->ending_lba + 1; - if (0 != AllocAndReadGptData(params->disk_handle, &gpt)) { - VBDEBUG(("Unable to read GPT data\n")); - shcall->check_result = VBSD_LKC_CHECK_GPT_READ_ERROR; - break; - } - - /* Initialize GPT library */ - if (GPT_SUCCESS != GptInit(&gpt)) { - VBDEBUG(("Error parsing GPT\n")); - shcall->check_result = VBSD_LKC_CHECK_GPT_PARSE_ERROR; - break; - } - - /* Allocate kernel header buffers */ - kbuf = (uint8_t*)VbExMalloc(KBUF_SIZE); - if (!kbuf) - break; - - /* Loop over candidate kernel partitions */ - while (GPT_SUCCESS == GptNextKernelEntry(&gpt, &part_start, &part_size)) { - VbSharedDataKernelPart* shpart = NULL; - VbKeyBlockHeader* key_block; - VbKernelPreambleHeader* preamble; - RSAPublicKey* data_key = NULL; - uint64_t key_version; - uint32_t combined_version; - uint64_t body_offset; - uint64_t body_offset_sectors; - uint64_t body_sectors; - int key_block_valid = 1; - - VBDEBUG(("Found kernel entry at %" PRIu64 " size %" PRIu64 "\n", - part_start, part_size)); - - /* Set up tracking for this partition. This wraps around if called - * many times, so initialize the partition entry each time. */ - shpart = shcall->parts + (shcall->kernel_parts_found - & (VBSD_MAX_KERNEL_PARTS - 1)); - Memset(shpart, 0, sizeof(VbSharedDataKernelPart)); - shpart->sector_start = part_start; - shpart->sector_count = part_size; - /* TODO: GPT partitions start at 1, but cgptlib starts them at 0. - * Adjust here, until cgptlib is fixed. */ - shpart->gpt_index = (uint8_t)(gpt.current_kernel + 1); - shcall->kernel_parts_found++; - - /* Found at least one kernel partition. */ - found_partitions++; - - /* Read the first part of the kernel partition. */ - if (part_size < kbuf_sectors) { - VBDEBUG(("Partition too small to hold kernel.\n")); - shpart->check_result = VBSD_LKP_CHECK_TOO_SMALL; - goto bad_kernel; - } - - if (0 != VbExDiskRead(params->disk_handle, part_start, kbuf_sectors, - kbuf)) { - VBDEBUG(("Unable to read start of partition.\n")); - shpart->check_result = VBSD_LKP_CHECK_READ_START; - goto bad_kernel; - } +VbError_t LoadKernel(LoadKernelParams *params) +{ + VbSharedDataHeader *shared = + (VbSharedDataHeader *)params->shared_data_blob; + VbSharedDataKernelCall *shcall = NULL; + VbNvContext* vnc = params->nv_context; + GoogleBinaryBlockHeader* gbb = + (GoogleBinaryBlockHeader *)params->gbb_data; + VbPublicKey* kernel_subkey; + GptData gpt; + uint64_t part_start, part_size; + uint64_t blba; + uint64_t kbuf_sectors; + uint8_t* kbuf = NULL; + int found_partitions = 0; + int good_partition = -1; + int good_partition_key_block_valid = 0; + uint32_t lowest_version = LOWEST_TPM_VERSION; + int rec_switch, dev_switch; + BootMode boot_mode; + uint32_t require_official_os = 0; + + VbError_t retval = VBERROR_UNKNOWN; + int recovery = VBNV_RECOVERY_LK_UNSPECIFIED; + + /* Sanity Checks */ + if (!params->bytes_per_lba || + !params->ending_lba) { + VBDEBUG(("LoadKernel() called with invalid params\n")); + retval = VBERROR_INVALID_PARAMETER; + goto LoadKernelExit; + } + + /* Clear output params in case we fail */ + params->partition_number = 0; + params->bootloader_address = 0; + params->bootloader_size = 0; + + /* Calculate switch positions and boot mode */ + rec_switch = (BOOT_FLAG_RECOVERY & params->boot_flags ? 1 : 0); + dev_switch = (BOOT_FLAG_DEVELOPER & params->boot_flags ? 1 : 0); + if (rec_switch) { + boot_mode = kBootRecovery; + } else if (dev_switch) { + boot_mode = kBootDev; + VbNvGet(vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &require_official_os); + } else { + boot_mode = kBootNormal; + } + + /* + * Set up tracking for this call. This wraps around if called many + * times, so we need to initialize the call entry each time. + */ + shcall = shared->lk_calls + (shared->lk_call_count + & (VBSD_MAX_KERNEL_CALLS - 1)); + Memset(shcall, 0, sizeof(VbSharedDataKernelCall)); + shcall->boot_flags = (uint32_t)params->boot_flags; + shcall->boot_mode = boot_mode; + shcall->sector_size = (uint32_t)params->bytes_per_lba; + shcall->sector_count = params->ending_lba + 1; + shared->lk_call_count++; + + /* Initialization */ + blba = params->bytes_per_lba; + kbuf_sectors = KBUF_SIZE / blba; + if (0 == kbuf_sectors) { + VBDEBUG(("LoadKernel() called with sector size > KBUF_SIZE\n")); + retval = VBERROR_INVALID_PARAMETER; + goto LoadKernelExit; + } + + if (kBootRecovery == boot_mode) { + /* Use the recovery key to verify the kernel */ + kernel_subkey = (VbPublicKey*) + ((uint8_t*)gbb + gbb->recovery_key_offset); + } else { + /* Use the kernel subkey passed from LoadFirmware(). */ + kernel_subkey = &shared->kernel_subkey; + } + + /* Read GPT data */ + gpt.sector_bytes = (uint32_t)blba; + gpt.drive_sectors = params->ending_lba + 1; + if (0 != AllocAndReadGptData(params->disk_handle, &gpt)) { + VBDEBUG(("Unable to read GPT data\n")); + shcall->check_result = VBSD_LKC_CHECK_GPT_READ_ERROR; + goto bad_gpt; + } + + /* Initialize GPT library */ + if (GPT_SUCCESS != GptInit(&gpt)) { + VBDEBUG(("Error parsing GPT\n")); + shcall->check_result = VBSD_LKC_CHECK_GPT_PARSE_ERROR; + goto bad_gpt; + } + + /* Allocate kernel header buffers */ + kbuf = (uint8_t*)VbExMalloc(KBUF_SIZE); + if (!kbuf) + goto bad_gpt; + + /* Loop over candidate kernel partitions */ + while (GPT_SUCCESS == + GptNextKernelEntry(&gpt, &part_start, &part_size)) { + VbSharedDataKernelPart *shpart = NULL; + VbKeyBlockHeader *key_block; + VbKernelPreambleHeader *preamble; + RSAPublicKey *data_key = NULL; + uint64_t key_version; + uint32_t combined_version; + uint64_t body_offset; + uint64_t body_offset_sectors; + uint64_t body_sectors; + int key_block_valid = 1; + + VBDEBUG(("Found kernel entry at %" PRIu64 " size %" PRIu64 "\n", + part_start, part_size)); + + /* + * Set up tracking for this partition. This wraps around if + * called many times, so initialize the partition entry each + * time. + */ + shpart = shcall->parts + (shcall->kernel_parts_found + & (VBSD_MAX_KERNEL_PARTS - 1)); + Memset(shpart, 0, sizeof(VbSharedDataKernelPart)); + shpart->sector_start = part_start; + shpart->sector_count = part_size; + /* + * TODO: GPT partitions start at 1, but cgptlib starts them at + * 0. Adjust here, until cgptlib is fixed. + */ + shpart->gpt_index = (uint8_t)(gpt.current_kernel + 1); + shcall->kernel_parts_found++; + + /* Found at least one kernel partition. */ + found_partitions++; + + /* Read the first part of the kernel partition. */ + if (part_size < kbuf_sectors) { + VBDEBUG(("Partition too small to hold kernel.\n")); + shpart->check_result = VBSD_LKP_CHECK_TOO_SMALL; + goto bad_kernel; + } + + if (0 != VbExDiskRead(params->disk_handle, part_start, + kbuf_sectors, kbuf)) { + VBDEBUG(("Unable to read start of partition.\n")); + shpart->check_result = VBSD_LKP_CHECK_READ_START; + goto bad_kernel; + } #if defined(CONFIG_SANDBOX) - /* Silence compiler warnings */ - combined_version = 0; - body_offset = body_offset; - body_offset_sectors = body_offset_sectors; - body_sectors = body_sectors; - kernel_subkey = kernel_subkey; - key_block = key_block; - key_version = key_version; - preamble = preamble; + /* Silence compiler warnings */ + combined_version = 0; + body_offset = body_offset; + body_offset_sectors = body_offset_sectors; + body_sectors = body_sectors; + kernel_subkey = kernel_subkey; + key_block = key_block; + key_version = key_version; + preamble = preamble; #else - /* Verify the key block. */ - key_block = (VbKeyBlockHeader*)kbuf; - if (0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey, 0)) { - VBDEBUG(("Verifying key block signature failed.\n")); - shpart->check_result = VBSD_LKP_CHECK_KEY_BLOCK_SIG; - - key_block_valid = 0; - - /* If we're not in developer mode, this kernel is bad. */ - if (kBootDev != boot_mode) - goto bad_kernel; - - /* In developer mode, we can explictly disallow self-signed kernels */ - if (require_official_os) { - VBDEBUG(("Self-signed custom kernels are not enabled.\n")); - shpart->check_result = VBSD_LKP_CHECK_SELF_SIGNED; - goto bad_kernel; - } - - /* Allow the kernel if the SHA-512 hash of the key block is valid. */ - if (0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey, 1)) { - VBDEBUG(("Verifying key block hash failed.\n")); - shpart->check_result = VBSD_LKP_CHECK_KEY_BLOCK_HASH; - goto bad_kernel; - } - } - - /* Check the key block flags against the current boot mode. */ - if (!(key_block->key_block_flags & - (dev_switch ? KEY_BLOCK_FLAG_DEVELOPER_1 : - KEY_BLOCK_FLAG_DEVELOPER_0))) { - VBDEBUG(("Key block developer flag mismatch.\n")); - shpart->check_result = VBSD_LKP_CHECK_DEV_MISMATCH; - key_block_valid = 0; - } - if (!(key_block->key_block_flags & - (rec_switch ? KEY_BLOCK_FLAG_RECOVERY_1 : - KEY_BLOCK_FLAG_RECOVERY_0))) { - VBDEBUG(("Key block recovery flag mismatch.\n")); - shpart->check_result = VBSD_LKP_CHECK_REC_MISMATCH; - key_block_valid = 0; - } - - /* Check for rollback of key version except in recovery mode. */ - key_version = key_block->data_key.key_version; - if (kBootRecovery != boot_mode) { - if (key_version < (shared->kernel_version_tpm >> 16)) { - VBDEBUG(("Key version too old.\n")); - shpart->check_result = VBSD_LKP_CHECK_KEY_ROLLBACK; - key_block_valid = 0; - } - if (key_version > 0xFFFF) { - /* Key version is stored in 16 bits in the TPM, so key versions - * greater than 0xFFFF can't be stored properly. */ - VBDEBUG(("Key version > 0xFFFF.\n")); - shpart->check_result = VBSD_LKP_CHECK_KEY_ROLLBACK; - key_block_valid = 0; - } - } - - /* If we're not in developer mode, require the key block to be valid. */ - if (kBootDev != boot_mode && !key_block_valid) { - VBDEBUG(("Key block is invalid.\n")); - goto bad_kernel; - } - - /* Get the key for preamble/data verification from the key block. */ - data_key = PublicKeyToRSA(&key_block->data_key); - if (!data_key) { - VBDEBUG(("Data key bad.\n")); - shpart->check_result = VBSD_LKP_CHECK_DATA_KEY_PARSE; - goto bad_kernel; - } - - /* Verify the preamble, which follows the key block */ - preamble = (VbKernelPreambleHeader*)(kbuf + key_block->key_block_size); - if ((0 != VerifyKernelPreamble(preamble, - KBUF_SIZE - key_block->key_block_size, - data_key))) { - VBDEBUG(("Preamble verification failed.\n")); - shpart->check_result = VBSD_LKP_CHECK_VERIFY_PREAMBLE; - goto bad_kernel; - } - - /* If the key block is valid and we're not in recovery mode, check for - * rollback of the kernel version. */ - combined_version = (uint32_t)((key_version << 16) | - (preamble->kernel_version & 0xFFFF)); - shpart->combined_version = combined_version; - if (key_block_valid && kBootRecovery != boot_mode) { - if (combined_version < shared->kernel_version_tpm) { - VBDEBUG(("Kernel version too low.\n")); - shpart->check_result = VBSD_LKP_CHECK_KERNEL_ROLLBACK; - /* If we're not in developer mode, kernel version must be valid. */ - if (kBootDev != boot_mode) - goto bad_kernel; - } - } - - VBDEBUG(("Kernel preamble is good.\n")); - shpart->check_result = VBSD_LKP_CHECK_PREAMBLE_VALID; - - /* Check for lowest version from a valid header. */ - if (key_block_valid && lowest_version > combined_version) - lowest_version = combined_version; - else { - VBDEBUG(("Key block valid: %d\n", key_block_valid)); - VBDEBUG(("Combined version: %u\n", (unsigned) combined_version)); - } - - /* If we already have a good kernel, no need to read another - * one; we only needed to look at the versions to check for - * rollback. So skip to the next kernel preamble. */ - if (-1 != good_partition) - continue; - - /* Verify kernel body starts at a multiple of the sector size. */ - body_offset = key_block->key_block_size + preamble->preamble_size; - if (0 != body_offset % blba) { - VBDEBUG(("Kernel body not at multiple of sector size.\n")); - shpart->check_result = VBSD_LKP_CHECK_BODY_OFFSET; - goto bad_kernel; - } - body_offset_sectors = body_offset / blba; - - body_sectors = (preamble->body_signature.data_size + blba - 1) / blba; - if (!params->kernel_buffer) { - /* Get kernel load address and size from the header. */ - params->kernel_buffer = (void*) ((long)preamble->body_load_address); - params->kernel_buffer_size = body_sectors * blba; - } else { - /* Verify kernel body fits in the buffer */ - if (body_sectors * blba > params->kernel_buffer_size) { - VBDEBUG(("Kernel body doesn't fit in memory.\n")); - shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_MEM; - goto bad_kernel; - } - } - - /* Verify kernel body fits in the partition */ - if (body_offset_sectors + body_sectors > part_size) { - VBDEBUG(("Kernel body doesn't fit in partition.\n")); - shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_PART; - goto bad_kernel; - } - - /* Read the kernel data */ - VBPERFSTART("VB_RKD"); - if (0 != VbExDiskRead(params->disk_handle, - part_start + body_offset_sectors, - body_sectors, params->kernel_buffer)) { - VBDEBUG(("Unable to read kernel data.\n")); - VBPERFEND("VB_RKD"); - shpart->check_result = VBSD_LKP_CHECK_READ_DATA; - goto bad_kernel; - } - VBPERFEND("VB_RKD"); - - /* Verify kernel data */ - if (0 != VerifyData((const uint8_t*)params->kernel_buffer, - params->kernel_buffer_size, - &preamble->body_signature, data_key)) { - VBDEBUG(("Kernel data verification failed.\n")); - shpart->check_result = VBSD_LKP_CHECK_VERIFY_DATA; - goto bad_kernel; - } - - /* Done with the kernel signing key, so can free it now */ - RSAPublicKeyFree(data_key); - data_key = NULL; + /* Verify the key block. */ + key_block = (VbKeyBlockHeader*)kbuf; + if (0 != KeyBlockVerify(key_block, KBUF_SIZE, + kernel_subkey, 0)) { + VBDEBUG(("Verifying key block signature failed.\n")); + shpart->check_result = VBSD_LKP_CHECK_KEY_BLOCK_SIG; + key_block_valid = 0; + + /* If not in developer mode, this kernel is bad. */ + if (kBootDev != boot_mode) + goto bad_kernel; + + /* + * In developer mode, we can explictly disallow + * self-signed kernels + */ + if (require_official_os) { + VBDEBUG(("Self-signed kernels not enabled.\n")); + shpart->check_result = + VBSD_LKP_CHECK_SELF_SIGNED; + goto bad_kernel; + } + + /* + * Allow the kernel if the SHA-512 hash of the key + * block is valid. + */ + if (0 != KeyBlockVerify(key_block, KBUF_SIZE, + kernel_subkey, 1)) { + VBDEBUG(("Verifying key block hash failed.\n")); + shpart->check_result = + VBSD_LKP_CHECK_KEY_BLOCK_HASH; + goto bad_kernel; + } + } + + /* Check the key block flags against the current boot mode. */ + if (!(key_block->key_block_flags & + (dev_switch ? KEY_BLOCK_FLAG_DEVELOPER_1 : + KEY_BLOCK_FLAG_DEVELOPER_0))) { + VBDEBUG(("Key block developer flag mismatch.\n")); + shpart->check_result = VBSD_LKP_CHECK_DEV_MISMATCH; + key_block_valid = 0; + } + if (!(key_block->key_block_flags & + (rec_switch ? KEY_BLOCK_FLAG_RECOVERY_1 : + KEY_BLOCK_FLAG_RECOVERY_0))) { + VBDEBUG(("Key block recovery flag mismatch.\n")); + shpart->check_result = VBSD_LKP_CHECK_REC_MISMATCH; + key_block_valid = 0; + } + + /* Check for rollback of key version except in recovery mode. */ + key_version = key_block->data_key.key_version; + if (kBootRecovery != boot_mode) { + if (key_version < (shared->kernel_version_tpm >> 16)) { + VBDEBUG(("Key version too old.\n")); + shpart->check_result = + VBSD_LKP_CHECK_KEY_ROLLBACK; + key_block_valid = 0; + } + if (key_version > 0xFFFF) { + /* + * Key version is stored in 16 bits in the TPM, + * so key versions greater than 0xFFFF can't be + * stored properly. + */ + VBDEBUG(("Key version > 0xFFFF.\n")); + shpart->check_result = + VBSD_LKP_CHECK_KEY_ROLLBACK; + key_block_valid = 0; + } + } + + /* If not in developer mode, key block required to be valid. */ + if (kBootDev != boot_mode && !key_block_valid) { + VBDEBUG(("Key block is invalid.\n")); + goto bad_kernel; + } + + /* Get key for preamble/data verification from the key block. */ + data_key = PublicKeyToRSA(&key_block->data_key); + if (!data_key) { + VBDEBUG(("Data key bad.\n")); + shpart->check_result = VBSD_LKP_CHECK_DATA_KEY_PARSE; + goto bad_kernel; + } + + /* Verify the preamble, which follows the key block */ + preamble = (VbKernelPreambleHeader *) + (kbuf + key_block->key_block_size); + if ((0 != VerifyKernelPreamble( + preamble, + KBUF_SIZE - key_block->key_block_size, + data_key))) { + VBDEBUG(("Preamble verification failed.\n")); + shpart->check_result = VBSD_LKP_CHECK_VERIFY_PREAMBLE; + goto bad_kernel; + } + + /* + * If the key block is valid and we're not in recovery mode, + * check for rollback of the kernel version. + */ + combined_version = (uint32_t)( + (key_version << 16) | + (preamble->kernel_version & 0xFFFF)); + shpart->combined_version = combined_version; + if (key_block_valid && kBootRecovery != boot_mode) { + if (combined_version < shared->kernel_version_tpm) { + VBDEBUG(("Kernel version too low.\n")); + shpart->check_result = + VBSD_LKP_CHECK_KERNEL_ROLLBACK; + /* + * If not in developer mode, kernel version + * must be valid. + */ + if (kBootDev != boot_mode) + goto bad_kernel; + } + } + + VBDEBUG(("Kernel preamble is good.\n")); + shpart->check_result = VBSD_LKP_CHECK_PREAMBLE_VALID; + + /* Check for lowest version from a valid header. */ + if (key_block_valid && lowest_version > combined_version) + lowest_version = combined_version; + else { + VBDEBUG(("Key block valid: %d\n", key_block_valid)); + VBDEBUG(("Combined version: %u\n", + (unsigned) combined_version)); + } + + /* + * If we already have a good kernel, no need to read another + * one; we only needed to look at the versions to check for + * rollback. So skip to the next kernel preamble. + */ + if (-1 != good_partition) + continue; + + /* Verify kernel body starts at multiple of sector size. */ + body_offset = key_block->key_block_size + + preamble->preamble_size; + if (0 != body_offset % blba) { + VBDEBUG(("Kernel body not at multiple of " + "sector size.\n")); + shpart->check_result = VBSD_LKP_CHECK_BODY_OFFSET; + goto bad_kernel; + } + body_offset_sectors = body_offset / blba; + + body_sectors = + (preamble->body_signature.data_size + blba - 1) / blba; + if (!params->kernel_buffer) { + /* Get kernel load address and size from the header. */ + params->kernel_buffer = + (void *)((long)preamble->body_load_address); + params->kernel_buffer_size = body_sectors * blba; + } else { + /* Verify kernel body fits in the buffer */ + if (body_sectors * blba > params->kernel_buffer_size) { + VBDEBUG(("Kernel body doesn't " + "fit in memory.\n")); + shpart->check_result = + VBSD_LKP_CHECK_BODY_EXCEEDS_MEM; + goto bad_kernel; + } + } + + /* Verify kernel body fits in the partition */ + if (body_offset_sectors + body_sectors > part_size) { + VBDEBUG(("Kernel body doesn't fit in partition.\n")); + shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_PART; + goto bad_kernel; + } + + /* Read the kernel data */ + VBPERFSTART("VB_RKD"); + if (0 != VbExDiskRead(params->disk_handle, + part_start + body_offset_sectors, + body_sectors, params->kernel_buffer)) { + VBDEBUG(("Unable to read kernel data.\n")); + VBPERFEND("VB_RKD"); + shpart->check_result = VBSD_LKP_CHECK_READ_DATA; + goto bad_kernel; + } + VBPERFEND("VB_RKD"); + + /* Verify kernel data */ + if (0 != VerifyData((const uint8_t *)params->kernel_buffer, + params->kernel_buffer_size, + &preamble->body_signature, data_key)) { + VBDEBUG(("Kernel data verification failed.\n")); + shpart->check_result = VBSD_LKP_CHECK_VERIFY_DATA; + goto bad_kernel; + } + + /* Done with the kernel signing key, so can free it now */ + RSAPublicKeyFree(data_key); + data_key = NULL; #endif - /* If we're still here, the kernel is valid. */ - /* Save the first good partition we find; that's the one we'll boot */ - VBDEBUG(("Partition is good.\n")); - shpart->check_result = VBSD_LKP_CHECK_KERNEL_GOOD; - if (key_block_valid) - shpart->flags |= VBSD_LKP_FLAG_KEY_BLOCK_VALID; - - good_partition_key_block_valid = key_block_valid; - /* TODO: GPT partitions start at 1, but cgptlib starts them at 0. - * Adjust here, until cgptlib is fixed. */ - good_partition = gpt.current_kernel + 1; - params->partition_number = gpt.current_kernel + 1; - GetCurrentKernelUniqueGuid(&gpt, ¶ms->partition_guid); - /* TODO: GetCurrentKernelUniqueGuid() should take a destination size, or - * the dest should be a struct, so we know it's big enough. */ + /* + * If we're still here, the kernel is valid. Save the first + * good partition we find; that's the one we'll boot. + */ + VBDEBUG(("Partition is good.\n")); + shpart->check_result = VBSD_LKP_CHECK_KERNEL_GOOD; + if (key_block_valid) + shpart->flags |= VBSD_LKP_FLAG_KEY_BLOCK_VALID; + + good_partition_key_block_valid = key_block_valid; + /* + * TODO: GPT partitions start at 1, but cgptlib starts them at + * 0. Adjust here, until cgptlib is fixed. + */ + good_partition = gpt.current_kernel + 1; + params->partition_number = gpt.current_kernel + 1; + GetCurrentKernelUniqueGuid(&gpt, ¶ms->partition_guid); + /* + * TODO: GetCurrentKernelUniqueGuid() should take a destination + * size, or the dest should be a struct, so we know it's big + * enough. + */ #if defined(CONFIG_SANDBOX) - params->bootloader_address = 0; - params->bootloader_size = 0; + params->bootloader_address = 0; + params->bootloader_size = 0; #else - params->bootloader_address = preamble->bootloader_address; - params->bootloader_size = preamble->bootloader_size; + params->bootloader_address = preamble->bootloader_address; + params->bootloader_size = preamble->bootloader_size; #endif - /* Update GPT to note this is the kernel we're trying */ - GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_TRY); - - /* If we're in recovery mode or we're about to boot a dev-signed kernel, - * there's no rollback protection, so we can stop at the first valid - * kernel. */ - if (kBootRecovery == boot_mode || !key_block_valid) { - VBDEBUG(("In recovery mode or dev-signed kernel\n")); - break; - } - - /* Otherwise, we do care about the key index in the TPM. If the good - * partition's key version is the same as the tpm, then the TPM doesn't - * need updating; we can stop now. Otherwise, we'll check all the other - * headers to see if they contain a newer key. */ - if (combined_version == shared->kernel_version_tpm) { - VBDEBUG(("Same kernel version\n")); - break; - } - - /* Continue, so that we skip the error handling code below */ - continue; - - bad_kernel: - /* Handle errors parsing this kernel */ - if (NULL != data_key) - RSAPublicKeyFree(data_key); - - VBDEBUG(("Marking kernel as invalid.\n")); - GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD); - - - } /* while(GptNextKernelEntry) */ - } while(0); - - /* Free kernel buffer */ - if (kbuf) - VbExFree(kbuf); - - /* Write and free GPT data */ - WriteAndFreeGptData(params->disk_handle, &gpt); - - /* Handle finding a good partition */ - if (good_partition >= 0) { - VBDEBUG(("Good_partition >= 0\n")); - shcall->check_result = VBSD_LKC_CHECK_GOOD_PARTITION; - shared->kernel_version_lowest = lowest_version; - /* Sanity check - only store a new TPM version if we found one. - * If lowest_version is still at its initial value, we didn't find - * one; for example, we're in developer mode and just didn't look. */ - if (lowest_version != LOWEST_TPM_VERSION && - lowest_version > shared->kernel_version_tpm) - shared->kernel_version_tpm = lowest_version; - - /* Success! */ - retval = VBERROR_SUCCESS; - } else if (found_partitions > 0) { - shcall->check_result = VBSD_LKC_CHECK_INVALID_PARTITIONS; - recovery = VBNV_RECOVERY_RW_INVALID_OS; - retval = VBERROR_INVALID_KERNEL_FOUND; - } else { - shcall->check_result = VBSD_LKC_CHECK_NO_PARTITIONS; - recovery = VBNV_RECOVERY_RW_NO_OS; - retval = VBERROR_NO_KERNEL_FOUND; - } - -LoadKernelExit: - - /* Store recovery request, if any */ - VbNvSet(vnc, VBNV_RECOVERY_REQUEST, VBERROR_SUCCESS != retval ? - recovery : VBNV_RECOVERY_NOT_REQUESTED); - - /* If LoadKernel was called with bad parameters, - * shcall may not be initialized. */ - if (shcall) - shcall->return_code = (uint8_t)retval; - - /* Save whether the good partition's key block was fully verified */ - if (good_partition_key_block_valid) - shared->flags |= VBSD_KERNEL_KEY_VERIFIED; - - /* Store how much shared data we used, if any */ - params->shared_data_size = shared->data_used; - - return retval; + /* Update GPT to note this is the kernel we're trying */ + GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_TRY); + + /* + * If we're in recovery mode or we're about to boot a + * dev-signed kernel, there's no rollback protection, so we can + * stop at the first valid kernel. + */ + if (kBootRecovery == boot_mode || !key_block_valid) { + VBDEBUG(("In recovery mode or dev-signed kernel\n")); + break; + } + + /* + * Otherwise, we do care about the key index in the TPM. If + * the good partition's key version is the same as the tpm, + * then the TPM doesn't need updating; we can stop now. + * Otherwise, we'll check all the other headers to see if they + * contain a newer key. + */ + if (combined_version == shared->kernel_version_tpm) { + VBDEBUG(("Same kernel version\n")); + break; + } + + /* Continue, so that we skip the error handling code below */ + continue; + + bad_kernel: + /* Handle errors parsing this kernel */ + if (NULL != data_key) + RSAPublicKeyFree(data_key); + + VBDEBUG(("Marking kernel as invalid.\n")); + GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD); + + + } /* while(GptNextKernelEntry) */ + + bad_gpt: + + /* Free kernel buffer */ + if (kbuf) + VbExFree(kbuf); + + /* Write and free GPT data */ + WriteAndFreeGptData(params->disk_handle, &gpt); + + /* Handle finding a good partition */ + if (good_partition >= 0) { + VBDEBUG(("Good_partition >= 0\n")); + shcall->check_result = VBSD_LKC_CHECK_GOOD_PARTITION; + shared->kernel_version_lowest = lowest_version; + /* + * Sanity check - only store a new TPM version if we found one. + * If lowest_version is still at its initial value, we didn't + * find one; for example, we're in developer mode and just + * didn't look. + */ + if (lowest_version != LOWEST_TPM_VERSION && + lowest_version > shared->kernel_version_tpm) + shared->kernel_version_tpm = lowest_version; + + /* Success! */ + retval = VBERROR_SUCCESS; + } else if (found_partitions > 0) { + shcall->check_result = VBSD_LKC_CHECK_INVALID_PARTITIONS; + recovery = VBNV_RECOVERY_RW_INVALID_OS; + retval = VBERROR_INVALID_KERNEL_FOUND; + } else { + shcall->check_result = VBSD_LKC_CHECK_NO_PARTITIONS; + recovery = VBNV_RECOVERY_RW_NO_OS; + retval = VBERROR_NO_KERNEL_FOUND; + } + + LoadKernelExit: + + /* Store recovery request, if any */ + VbNvSet(vnc, VBNV_RECOVERY_REQUEST, VBERROR_SUCCESS != retval ? + recovery : VBNV_RECOVERY_NOT_REQUESTED); + + /* + * If LoadKernel() was called with bad parameters, shcall may not be + * initialized. + */ + if (shcall) + shcall->return_code = (uint8_t)retval; + + /* Save whether the good partition's key block was fully verified */ + if (good_partition_key_block_valid) + shared->flags |= VBSD_KERNEL_KEY_VERIFIED; + + /* Store how much shared data we used, if any */ + params->shared_data_size = shared->data_used; + + return retval; } diff --git a/firmware/lib/vboot_nvstorage.c b/firmware/lib/vboot_nvstorage.c index 3497392e..618c7124 100644 --- a/firmware/lib/vboot_nvstorage.c +++ b/firmware/lib/vboot_nvstorage.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. */ @@ -11,9 +11,10 @@ #include "vboot_common.h" #include "vboot_nvstorage.h" -/* Constants for NV storage. We use this rather than structs and - * bitfields so the data format is consistent across platforms and - * compilers. */ +/* + * Constants for NV storage. We use this rather than structs and bitfields so + * the data format is consistent across platforms and compilers. + */ #define HEADER_OFFSET 0 #define HEADER_MASK 0xC0 #define HEADER_SIGNATURE 0x40 @@ -43,235 +44,241 @@ #define KERNEL_FIELD_OFFSET 11 #define CRC_OFFSET 15 +int VbNvSetup(VbNvContext *context) +{ + uint8_t *raw = context->raw; -int VbNvSetup(VbNvContext* context) { - uint8_t* raw = context->raw; - - /* Nothing has changed yet. */ - context->raw_changed = 0; - context->regenerate_crc = 0; - - /* Check data for consistency */ - if ((HEADER_SIGNATURE != (raw[HEADER_OFFSET] & HEADER_MASK)) - || (Crc8(raw, CRC_OFFSET) != raw[CRC_OFFSET])) { + /* Nothing has changed yet. */ + context->raw_changed = 0; + context->regenerate_crc = 0; - /* Data is inconsistent (bad CRC or header), so reset defaults */ - Memset(raw, 0, VBNV_BLOCK_SIZE); - raw[HEADER_OFFSET] = (HEADER_SIGNATURE | HEADER_FIRMWARE_SETTINGS_RESET | - HEADER_KERNEL_SETTINGS_RESET); + /* Check data for consistency */ + if ((HEADER_SIGNATURE != (raw[HEADER_OFFSET] & HEADER_MASK)) + || (Crc8(raw, CRC_OFFSET) != raw[CRC_OFFSET])) { + /* Data is inconsistent (bad CRC or header); reset defaults */ + Memset(raw, 0, VBNV_BLOCK_SIZE); + raw[HEADER_OFFSET] = (HEADER_SIGNATURE | + HEADER_FIRMWARE_SETTINGS_RESET | + HEADER_KERNEL_SETTINGS_RESET); - /* Regenerate CRC on exit */ - context->regenerate_crc = 1; - } + /* Regenerate CRC on exit */ + context->regenerate_crc = 1; + } - return 0; + return 0; } +int VbNvTeardown(VbNvContext *context) +{ + if (context->regenerate_crc) { + context->raw[CRC_OFFSET] = Crc8(context->raw, CRC_OFFSET); + context->regenerate_crc = 0; + context->raw_changed = 1; + } -int VbNvTeardown(VbNvContext* context) { - - if (context->regenerate_crc) { - context->raw[CRC_OFFSET] = Crc8(context->raw, CRC_OFFSET); - context->regenerate_crc = 0; - context->raw_changed = 1; - } - - return 0; + return 0; } - -int VbNvGet(VbNvContext* context, VbNvParam param, uint32_t* dest) { - const uint8_t* raw = context->raw; - - switch (param) { - case VBNV_FIRMWARE_SETTINGS_RESET: - *dest = (raw[HEADER_OFFSET] & HEADER_FIRMWARE_SETTINGS_RESET ? 1 : 0); - return 0; - - case VBNV_KERNEL_SETTINGS_RESET: - *dest = (raw[HEADER_OFFSET] & HEADER_KERNEL_SETTINGS_RESET ? 1 : 0); - return 0; - - case VBNV_DEBUG_RESET_MODE: - *dest = (raw[BOOT_OFFSET] & BOOT_DEBUG_RESET_MODE ? 1 : 0); - return 0; - - case VBNV_TRY_B_COUNT: - *dest = raw[BOOT_OFFSET] & BOOT_TRY_B_COUNT_MASK; - return 0; - - case VBNV_RECOVERY_REQUEST: - *dest = raw[RECOVERY_OFFSET]; - return 0; - - case VBNV_RECOVERY_SUBCODE: - *dest = raw[RECOVERY_SUBCODE_OFFSET]; - return 0; - - case VBNV_LOCALIZATION_INDEX: - *dest = raw[LOCALIZATION_OFFSET]; - return 0; - - case VBNV_KERNEL_FIELD: - *dest = (raw[KERNEL_FIELD_OFFSET] - | (raw[KERNEL_FIELD_OFFSET + 1] << 8) - | (raw[KERNEL_FIELD_OFFSET + 2] << 16) - | (raw[KERNEL_FIELD_OFFSET + 3] << 24)); - return 0; - - case VBNV_DEV_BOOT_USB: - *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_USB_MASK ? 1 : 0); - return 0; - - case VBNV_DEV_BOOT_LEGACY: - *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_LEGACY_MASK ? 1 : 0); - return 0; - - case VBNV_DEV_BOOT_SIGNED_ONLY: - *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_SIGNED_ONLY_MASK ? 1 : 0); - return 0; - - case VBNV_DISABLE_DEV_REQUEST: - *dest = (raw[BOOT_OFFSET] & BOOT_DISABLE_DEV_REQUEST ? 1 : 0); - return 0; - - case VBNV_OPROM_NEEDED: - *dest = (raw[BOOT_OFFSET] & BOOT_OPROM_NEEDED ? 1 : 0); - return 0; - - case VBNV_CLEAR_TPM_OWNER_REQUEST: - *dest = (raw[TPM_FLAGS_OFFSET] & TPM_CLEAR_OWNER_REQUEST ? 1 : 0); - return 0; - - case VBNV_CLEAR_TPM_OWNER_DONE: - *dest = (raw[TPM_FLAGS_OFFSET] & TPM_CLEAR_OWNER_DONE ? 1 : 0); - return 0; - - default: - return 1; - } +int VbNvGet(VbNvContext *context, VbNvParam param, uint32_t *dest) +{ + const uint8_t *raw = context->raw; + + switch (param) { + case VBNV_FIRMWARE_SETTINGS_RESET: + *dest = (raw[HEADER_OFFSET] & HEADER_FIRMWARE_SETTINGS_RESET ? + 1 : 0); + return 0; + + case VBNV_KERNEL_SETTINGS_RESET: + *dest = (raw[HEADER_OFFSET] & HEADER_KERNEL_SETTINGS_RESET ? + 1 : 0); + return 0; + + case VBNV_DEBUG_RESET_MODE: + *dest = (raw[BOOT_OFFSET] & BOOT_DEBUG_RESET_MODE ? 1 : 0); + return 0; + + case VBNV_TRY_B_COUNT: + *dest = raw[BOOT_OFFSET] & BOOT_TRY_B_COUNT_MASK; + return 0; + + case VBNV_RECOVERY_REQUEST: + *dest = raw[RECOVERY_OFFSET]; + return 0; + + case VBNV_RECOVERY_SUBCODE: + *dest = raw[RECOVERY_SUBCODE_OFFSET]; + return 0; + + case VBNV_LOCALIZATION_INDEX: + *dest = raw[LOCALIZATION_OFFSET]; + return 0; + + case VBNV_KERNEL_FIELD: + *dest = (raw[KERNEL_FIELD_OFFSET] + | (raw[KERNEL_FIELD_OFFSET + 1] << 8) + | (raw[KERNEL_FIELD_OFFSET + 2] << 16) + | (raw[KERNEL_FIELD_OFFSET + 3] << 24)); + return 0; + + case VBNV_DEV_BOOT_USB: + *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_USB_MASK ? 1 : 0); + return 0; + + case VBNV_DEV_BOOT_LEGACY: + *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_LEGACY_MASK ? 1 : 0); + return 0; + + case VBNV_DEV_BOOT_SIGNED_ONLY: + *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_SIGNED_ONLY_MASK ? + 1 : 0); + return 0; + + case VBNV_DISABLE_DEV_REQUEST: + *dest = (raw[BOOT_OFFSET] & BOOT_DISABLE_DEV_REQUEST ? 1 : 0); + return 0; + + case VBNV_OPROM_NEEDED: + *dest = (raw[BOOT_OFFSET] & BOOT_OPROM_NEEDED ? 1 : 0); + return 0; + + case VBNV_CLEAR_TPM_OWNER_REQUEST: + *dest = (raw[TPM_FLAGS_OFFSET] & TPM_CLEAR_OWNER_REQUEST ? + 1 : 0); + return 0; + + case VBNV_CLEAR_TPM_OWNER_DONE: + *dest = (raw[TPM_FLAGS_OFFSET] & TPM_CLEAR_OWNER_DONE ? 1 : 0); + return 0; + + default: + return 1; + } } - -int VbNvSet(VbNvContext* context, VbNvParam param, uint32_t value) { - uint8_t* raw = context->raw; - uint32_t current; - - /* If we're not changing the value, we don't need to regenerate the CRC. */ - if (0 == VbNvGet(context, param, ¤t) && current == value) - return 0; - - switch (param) { - case VBNV_FIRMWARE_SETTINGS_RESET: - if (value) - raw[HEADER_OFFSET] |= HEADER_FIRMWARE_SETTINGS_RESET; - else - raw[HEADER_OFFSET] &= ~HEADER_FIRMWARE_SETTINGS_RESET; - break; - - case VBNV_KERNEL_SETTINGS_RESET: - if (value) - raw[HEADER_OFFSET] |= HEADER_KERNEL_SETTINGS_RESET; - else - raw[HEADER_OFFSET] &= ~HEADER_KERNEL_SETTINGS_RESET; - break; - - case VBNV_DEBUG_RESET_MODE: - if (value) - raw[BOOT_OFFSET] |= BOOT_DEBUG_RESET_MODE; - else - raw[BOOT_OFFSET] &= ~BOOT_DEBUG_RESET_MODE; - break; - - case VBNV_TRY_B_COUNT: - /* Clip to valid range. */ - if (value > BOOT_TRY_B_COUNT_MASK) - value = BOOT_TRY_B_COUNT_MASK; - - raw[BOOT_OFFSET] &= ~BOOT_TRY_B_COUNT_MASK; - raw[BOOT_OFFSET] |= (uint8_t)value; - break; - - case VBNV_RECOVERY_REQUEST: - /* Map values outside the valid range to the legacy reason, since we - * can't determine if we're called from kernel or user mode. */ - if (value > 0xFF) - value = VBNV_RECOVERY_LEGACY; - raw[RECOVERY_OFFSET] = (uint8_t)value; - break; - - case VBNV_RECOVERY_SUBCODE: - raw[RECOVERY_SUBCODE_OFFSET] = (uint8_t)value; - break; - - case VBNV_LOCALIZATION_INDEX: - /* Map values outside the valid range to the default index. */ - if (value > 0xFF) - value = 0; - raw[LOCALIZATION_OFFSET] = (uint8_t)value; - break; - - case VBNV_KERNEL_FIELD: - raw[KERNEL_FIELD_OFFSET] = (uint8_t)(value); - raw[KERNEL_FIELD_OFFSET + 1] = (uint8_t)(value >> 8); - raw[KERNEL_FIELD_OFFSET + 2] = (uint8_t)(value >> 16); - raw[KERNEL_FIELD_OFFSET + 3] = (uint8_t)(value >> 24); - break; - - case VBNV_DEV_BOOT_USB: - if (value) - raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_USB_MASK; - else - raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_USB_MASK; - break; - - case VBNV_DEV_BOOT_LEGACY: - if (value) - raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_LEGACY_MASK; - else - raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_LEGACY_MASK; - break; - - case VBNV_DEV_BOOT_SIGNED_ONLY: - if (value) - raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_SIGNED_ONLY_MASK; - else - raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_SIGNED_ONLY_MASK; - break; - - case VBNV_DISABLE_DEV_REQUEST: - if (value) - raw[BOOT_OFFSET] |= BOOT_DISABLE_DEV_REQUEST; - else - raw[BOOT_OFFSET] &= ~BOOT_DISABLE_DEV_REQUEST; - break; - - case VBNV_OPROM_NEEDED: - if (value) - raw[BOOT_OFFSET] |= BOOT_OPROM_NEEDED; - else - raw[BOOT_OFFSET] &= ~BOOT_OPROM_NEEDED; - break; - - case VBNV_CLEAR_TPM_OWNER_REQUEST: - if (value) - raw[TPM_FLAGS_OFFSET] |= TPM_CLEAR_OWNER_REQUEST; - else - raw[TPM_FLAGS_OFFSET] &= ~TPM_CLEAR_OWNER_REQUEST; - break; - - case VBNV_CLEAR_TPM_OWNER_DONE: - if (value) - raw[TPM_FLAGS_OFFSET] |= TPM_CLEAR_OWNER_DONE; - else - raw[TPM_FLAGS_OFFSET] &= ~TPM_CLEAR_OWNER_DONE; - break; - - default: - return 1; - } - - /* Need to regenerate CRC, since the value changed. */ - context->regenerate_crc = 1; - return 0; +int VbNvSet(VbNvContext *context, VbNvParam param, uint32_t value) +{ + uint8_t *raw = context->raw; + uint32_t current; + + /* If not changing the value, don't regenerate the CRC. */ + if (0 == VbNvGet(context, param, ¤t) && current == value) + return 0; + + switch (param) { + case VBNV_FIRMWARE_SETTINGS_RESET: + if (value) + raw[HEADER_OFFSET] |= HEADER_FIRMWARE_SETTINGS_RESET; + else + raw[HEADER_OFFSET] &= ~HEADER_FIRMWARE_SETTINGS_RESET; + break; + + case VBNV_KERNEL_SETTINGS_RESET: + if (value) + raw[HEADER_OFFSET] |= HEADER_KERNEL_SETTINGS_RESET; + else + raw[HEADER_OFFSET] &= ~HEADER_KERNEL_SETTINGS_RESET; + break; + + case VBNV_DEBUG_RESET_MODE: + if (value) + raw[BOOT_OFFSET] |= BOOT_DEBUG_RESET_MODE; + else + raw[BOOT_OFFSET] &= ~BOOT_DEBUG_RESET_MODE; + break; + + case VBNV_TRY_B_COUNT: + /* Clip to valid range. */ + if (value > BOOT_TRY_B_COUNT_MASK) + value = BOOT_TRY_B_COUNT_MASK; + + raw[BOOT_OFFSET] &= ~BOOT_TRY_B_COUNT_MASK; + raw[BOOT_OFFSET] |= (uint8_t)value; + break; + + case VBNV_RECOVERY_REQUEST: + /* + * Map values outside the valid range to the legacy reason, + * since we can't determine if we're called from kernel or user + * mode. + */ + if (value > 0xFF) + value = VBNV_RECOVERY_LEGACY; + raw[RECOVERY_OFFSET] = (uint8_t)value; + break; + + case VBNV_RECOVERY_SUBCODE: + raw[RECOVERY_SUBCODE_OFFSET] = (uint8_t)value; + break; + + case VBNV_LOCALIZATION_INDEX: + /* Map values outside the valid range to the default index. */ + if (value > 0xFF) + value = 0; + raw[LOCALIZATION_OFFSET] = (uint8_t)value; + break; + + case VBNV_KERNEL_FIELD: + raw[KERNEL_FIELD_OFFSET] = (uint8_t)(value); + raw[KERNEL_FIELD_OFFSET + 1] = (uint8_t)(value >> 8); + raw[KERNEL_FIELD_OFFSET + 2] = (uint8_t)(value >> 16); + raw[KERNEL_FIELD_OFFSET + 3] = (uint8_t)(value >> 24); + break; + + case VBNV_DEV_BOOT_USB: + if (value) + raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_USB_MASK; + else + raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_USB_MASK; + break; + + case VBNV_DEV_BOOT_LEGACY: + if (value) + raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_LEGACY_MASK; + else + raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_LEGACY_MASK; + break; + + case VBNV_DEV_BOOT_SIGNED_ONLY: + if (value) + raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_SIGNED_ONLY_MASK; + else + raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_SIGNED_ONLY_MASK; + break; + + case VBNV_DISABLE_DEV_REQUEST: + if (value) + raw[BOOT_OFFSET] |= BOOT_DISABLE_DEV_REQUEST; + else + raw[BOOT_OFFSET] &= ~BOOT_DISABLE_DEV_REQUEST; + break; + + case VBNV_OPROM_NEEDED: + if (value) + raw[BOOT_OFFSET] |= BOOT_OPROM_NEEDED; + else + raw[BOOT_OFFSET] &= ~BOOT_OPROM_NEEDED; + break; + + case VBNV_CLEAR_TPM_OWNER_REQUEST: + if (value) + raw[TPM_FLAGS_OFFSET] |= TPM_CLEAR_OWNER_REQUEST; + else + raw[TPM_FLAGS_OFFSET] &= ~TPM_CLEAR_OWNER_REQUEST; + break; + + case VBNV_CLEAR_TPM_OWNER_DONE: + if (value) + raw[TPM_FLAGS_OFFSET] |= TPM_CLEAR_OWNER_DONE; + else + raw[TPM_FLAGS_OFFSET] &= ~TPM_CLEAR_OWNER_DONE; + break; + + default: + return 1; + } + + /* Need to regenerate CRC, since the value changed. */ + context->regenerate_crc = 1; + return 0; } diff --git a/firmware/stub/vboot_api_stub.c b/firmware/stub/vboot_api_stub.c index 8521db0e..c71c38ac 100644 --- a/firmware/stub/vboot_api_stub.c +++ b/firmware/stub/vboot_api_stub.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. * @@ -22,174 +22,198 @@ __pragma(warning (disable: 4100)) /* U-Boot's printf uses '%L' for uint64_t. gcc uses '%l'. */ #define MAX_FMT 255 static char fmtbuf[MAX_FMT+1]; -static const char *fixfmt(const char *format) { - int i; - for(i=0; i<MAX_FMT && format[i]; i++) { - fmtbuf[i] = format[i]; - if(format[i] == '%' && format[i+1] == 'L') { - fmtbuf[i+1] = 'l'; - i++; - } - } - fmtbuf[i] = '\0'; - return fmtbuf; -} -void VbExError(const char* format, ...) { - va_list ap; - va_start(ap, format); - fprintf(stderr, "ERROR: "); - vfprintf(stderr, fixfmt(format), ap); - va_end(ap); - exit(1); +static const char *fixfmt(const char *format) +{ + int i; + for(i=0; i<MAX_FMT && format[i]; i++) { + fmtbuf[i] = format[i]; + if(format[i] == '%' && format[i+1] == 'L') { + fmtbuf[i+1] = 'l'; + i++; + } + } + fmtbuf[i] = '\0'; + return fmtbuf; +} + +void VbExError(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + fprintf(stderr, "ERROR: "); + vfprintf(stderr, fixfmt(format), ap); + va_end(ap); + exit(1); } - -void VbExDebug(const char* format, ...) { - va_list ap; - va_start(ap, format); - fprintf(stderr, "DEBUG: "); - vfprintf(stderr, fixfmt(format), ap); - va_end(ap); +void VbExDebug(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + fprintf(stderr, "DEBUG: "); + vfprintf(stderr, fixfmt(format), ap); + va_end(ap); } - -void* VbExMalloc(size_t size) { - void* p = malloc(size); - if (!p) { - /* Fatal Error. We must abort. */ - abort(); - } - return p; +void *VbExMalloc(size_t size) +{ + void *p = malloc(size); + if (!p) { + /* Fatal Error. We must abort. */ + abort(); + } + return p; } - -void VbExFree(void* ptr) { - free(ptr); +void VbExFree(void *ptr) +{ + free(ptr); } - -uint64_t VbExGetTimer(void) { - struct timeval tv; - gettimeofday(&tv, NULL); - return (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec; +uint64_t VbExGetTimer(void) +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec; } - -void VbExSleepMs(uint32_t msec) { +void VbExSleepMs(uint32_t msec) +{ } - -VbError_t VbExBeep(uint32_t msec, uint32_t frequency) { - return VBERROR_SUCCESS; +VbError_t VbExBeep(uint32_t msec, uint32_t frequency) +{ + return VBERROR_SUCCESS; } - -VbError_t VbExNvStorageRead(uint8_t* buf) { - return VBERROR_SUCCESS; +VbError_t VbExNvStorageRead(uint8_t *buf) +{ + return VBERROR_SUCCESS; } - -VbError_t VbExNvStorageWrite(const uint8_t* buf) { - return VBERROR_SUCCESS; +VbError_t VbExNvStorageWrite(const uint8_t *buf) +{ + return VBERROR_SUCCESS; } - -VbError_t VbExHashFirmwareBody(VbCommonParams* cparams, - uint32_t firmware_index) { - return VBERROR_SUCCESS; +VbError_t VbExHashFirmwareBody(VbCommonParams *cparams, + uint32_t firmware_index) +{ + return VBERROR_SUCCESS; } - -VbError_t VbExDisplayInit(uint32_t* width, uint32_t* height) { - return VBERROR_SUCCESS; +VbError_t VbExDisplayInit(uint32_t *width, uint32_t *height) +{ + return VBERROR_SUCCESS; } - -VbError_t VbExDisplayBacklight(uint8_t enable) { - return VBERROR_SUCCESS; +VbError_t VbExDisplayBacklight(uint8_t enable) +{ + return VBERROR_SUCCESS; } - -VbError_t VbExDisplayScreen(uint32_t screen_type) { - return VBERROR_SUCCESS; +VbError_t VbExDisplayScreen(uint32_t screen_type) +{ + return VBERROR_SUCCESS; } - VbError_t VbExDisplayImage(uint32_t x, uint32_t y, - void* buffer, uint32_t buffersize) { - return VBERROR_SUCCESS; + void *buffer, uint32_t buffersize) +{ + return VBERROR_SUCCESS; } - -VbError_t VbExDisplayDebugInfo(const char* info_str) { - return VBERROR_SUCCESS; +VbError_t VbExDisplayDebugInfo(const char *info_str) +{ + return VBERROR_SUCCESS; } - -uint32_t VbExKeyboardRead(void) { - return 0; +uint32_t VbExKeyboardRead(void) +{ + return 0; } - -uint32_t VbExIsShutdownRequested(void) { - return 0; +uint32_t VbExIsShutdownRequested(void) +{ + return 0; } VbError_t VbExDecompress(void *inbuf, uint32_t in_size, uint32_t compression_type, - void *outbuf, uint32_t *out_size) { - return VBERROR_SUCCESS; + void *outbuf, uint32_t *out_size) +{ + return VBERROR_SUCCESS; } -int VbExTrustEC(void) { - return 1; +int VbExTrustEC(void) +{ + return 1; } -VbError_t VbExEcRunningRW(int *in_rw) { - *in_rw = 0; - return VBERROR_SUCCESS; +VbError_t VbExEcRunningRW(int *in_rw) +{ + *in_rw = 0; + return VBERROR_SUCCESS; } -VbError_t VbExEcJumpToRW(void) { - return VBERROR_SUCCESS; +VbError_t VbExEcJumpToRW(void) +{ + return VBERROR_SUCCESS; } -VbError_t VbExEcRebootToRO(void) { - /* Nothing to reboot, so all we can do is return failure. */ - return VBERROR_UNKNOWN; +VbError_t VbExEcRebootToRO(void) +{ + /* Nothing to reboot, so all we can do is return failure. */ + return VBERROR_UNKNOWN; } -VbError_t VbExEcStayInRO(void) { - return VBERROR_SUCCESS; +VbError_t VbExEcStayInRO(void) +{ + return VBERROR_SUCCESS; } #define SHA256_HASH_SIZE 32 -VbError_t VbExEcHashRW(const uint8_t **hash, int *hash_size) { - static const uint8_t fake_hash[32] = {1, 2, 3, 4}; - *hash = fake_hash; - *hash_size = sizeof(fake_hash); - return VBERROR_SUCCESS; +VbError_t VbExEcHashRW(const uint8_t **hash, int *hash_size) +{ + static const uint8_t fake_hash[32] = {1, 2, 3, 4}; + + *hash = fake_hash; + *hash_size = sizeof(fake_hash); + return VBERROR_SUCCESS; } VbError_t VbExEcGetExpectedRW(enum VbSelectFirmware_t select, - const uint8_t **image, int *image_size) { - static uint8_t fake_image[64] = {5, 6, 7, 8}; - *image = fake_image; - *image_size = sizeof(fake_image); - return VBERROR_SUCCESS; + const uint8_t **image, int *image_size) +{ + static uint8_t fake_image[64] = {5, 6, 7, 8}; + *image = fake_image; + *image_size = sizeof(fake_image); + return VBERROR_SUCCESS; } -VbError_t VbExEcUpdateRW(const uint8_t *image, int image_size) { - return VBERROR_SUCCESS; +VbError_t VbExEcGetExpectedRWHash(enum VbSelectFirmware_t select, + const uint8_t **hash, int *hash_size) +{ + static const uint8_t fake_hash[32] = {1, 2, 3, 4}; + + *hash = fake_hash; + *hash_size = sizeof(fake_hash); + return VBERROR_SUCCESS; +} + +VbError_t VbExEcUpdateRW(const uint8_t *image, int image_size) +{ + return VBERROR_SUCCESS; } -VbError_t VbExEcProtectRW(void) { - return VBERROR_SUCCESS; +VbError_t VbExEcProtectRW(void) +{ + return VBERROR_SUCCESS; } int VbExLegacy(void) { - return 1; + return 1; } diff --git a/futility/futility.c b/futility/futility.c index 483f4521..be8d3b08 100644 --- a/futility/futility.c +++ b/futility/futility.c @@ -102,9 +102,18 @@ static void log_str(char *str) static void log_close(void) { - if (log_fd >= 0) + struct flock lock; + + if (log_fd >= 0) { + memset(&lock, 0, sizeof(lock)); + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + if (fcntl(log_fd, F_SETLKW, &lock)) + perror("Unable to unlock log file"); + close(log_fd); - log_fd = -1; + log_fd = -1; + } } static void log_open(void) @@ -159,6 +168,7 @@ int main(int argc, char *argv[], char *envp[]) log_open(); for (i = 0; i < argc; i++) log_str(argv[i]); + log_close(); /* How were we invoked? */ progname = strrchr(argv[0], '/'); diff --git a/futility/setup_futility_symlinks.sh b/futility/setup_futility_symlinks.sh deleted file mode 100755 index d7db9a7e..00000000 --- a/futility/setup_futility_symlinks.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/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. - -# Fail on any unexpected nonsense. -set -e -u - -# The one required argument is the directory where futility lives. -BINDIR="$1" -shift - -# We look here to see what names to use for the symlinks. -OLDDIR="$BINDIR/old_bins" - -# Create the symlinks. -for prog in $OLDDIR/*; do - ln -sf futility "${BINDIR}/${prog##*/}" -done diff --git a/scripts/image_signing/common.sh b/scripts/image_signing/common.sh index 599c9e1e..599c9e1e 100755..100644 --- a/scripts/image_signing/common.sh +++ b/scripts/image_signing/common.sh diff --git a/scripts/image_signing/common_minimal.sh b/scripts/image_signing/common_minimal.sh index 576d2d8f..576d2d8f 100755..100644 --- a/scripts/image_signing/common_minimal.sh +++ b/scripts/image_signing/common_minimal.sh diff --git a/scripts/image_signing/sample-test-configs/ensure_no_nonrelease_files.config b/scripts/image_signing/sample-test-configs/ensure_no_nonrelease_files.config index 1ca02f1a..1ca02f1a 100755..100644 --- a/scripts/image_signing/sample-test-configs/ensure_no_nonrelease_files.config +++ b/scripts/image_signing/sample-test-configs/ensure_no_nonrelease_files.config diff --git a/scripts/image_signing/sample-test-configs/ensure_secure_kernelparams.config b/scripts/image_signing/sample-test-configs/ensure_secure_kernelparams.config index 9b78def0..9b78def0 100755..100644 --- a/scripts/image_signing/sample-test-configs/ensure_secure_kernelparams.config +++ b/scripts/image_signing/sample-test-configs/ensure_secure_kernelparams.config diff --git a/scripts/image_signing/set_lsb_release.sh b/scripts/image_signing/set_lsb_release.sh index 5d859d81..9d0addd0 100755 --- a/scripts/image_signing/set_lsb_release.sh +++ b/scripts/image_signing/set_lsb_release.sh @@ -24,12 +24,9 @@ set_lsb_release_keyval() { main() { set -e - local image=$1 - local key=$2 - local value=$3 - if [ $# -ne 1 ] && [ $# -ne 3 ]; then + if [[ $(( $# % 2 )) -eq 0 ]]; then cat <<EOF -Usage: $PROG <image.bin> [<key> <value>] +Usage: $PROG <image.bin> [<key> <value> [<key> <value> ...]] Examples: @@ -47,15 +44,28 @@ EOF exit 1 fi + local image=$1 + shift local rootfs=$(make_temp_dir) - mount_image_partition_ro "$image" 3 "$rootfs" - if [ -n "$key" ]; then - sudo umount "$rootfs" - mount_image_partition "$image" 3 "$rootfs" - set_lsb_release_keyval "$rootfs" "$key" "$value" - touch "$image" # Updates the image modification time. + + # If there are no key/value pairs to process, we don't need write access. + if [[ $# -eq 0 ]]; then + mount_image_partition_ro "${image}" 3 "${rootfs}" + else + mount_image_partition "${image}" 3 "${rootfs}" + touch "${image}" # Updates the image modification time. fi - cat "$rootfs/etc/lsb-release" + + # Process all the key/value pairs. + local key value + while [[ $# -ne 0 ]]; do + key=$1 value=$2 + shift 2 + set_lsb_release_keyval "${rootfs}" "${key}" "${value}" + done + + # Dump the final state. + cat "${rootfs}/etc/lsb-release" } main "$@" diff --git a/scripts/image_signing/sign_official_build.sh b/scripts/image_signing/sign_official_build.sh index eedfe622..492dd15b 100755 --- a/scripts/image_signing/sign_official_build.sh +++ b/scripts/image_signing/sign_official_build.sh @@ -129,7 +129,7 @@ get_dmparams_from_config() { get_hash_from_config() { local kernel_config=$1 local dm_config=$(get_dmparams_from_config "${kernel_config}") - local vroot_dev=$(get_dm_args "${dm_config}" vroot) + local vroot_dev=$(get_dm_slave "${dm_config}" vroot) if is_old_verity_argv "${vroot_dev}"; then echo ${vroot_dev} | cut -f9 -d ' ' else diff --git a/scripts/keygeneration/common.sh b/scripts/keygeneration/common.sh index c3c1c25d..c3c1c25d 100755..100644 --- a/scripts/keygeneration/common.sh +++ b/scripts/keygeneration/common.sh diff --git a/tests/common.sh b/tests/common.sh index 43cdf6d2..43cdf6d2 100755..100644 --- a/tests/common.sh +++ b/tests/common.sh diff --git a/tests/tlcl_tests.c b/tests/tlcl_tests.c new file mode 100644 index 00000000..4765cff3 --- /dev/null +++ b/tests/tlcl_tests.c @@ -0,0 +1,351 @@ +/* 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 TPM lite library + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <tss/tcs.h> +/* Don't use the vboot constants, since they conflict with the TCS lib */ +#define VBOOT_REFERENCE_TSS_CONSTANTS_H_ + +#include "host_common.h" +#include "test_common.h" +#include "tlcl.h" +#include "tlcl_internal.h" +#include "vboot_common.h" + +/* Mock data */ +static char debug_info[4096]; +static VbError_t mock_retval; + +/* Call to mocked VbExTpmSendReceive() */ +struct srcall +{ + const uint8_t *req; /* Request */ + uint8_t *rsp; /* Response */ + uint8_t rsp_buf[32]; /* Default response buffer, if not overridden */ + int req_size; /* Request size */ + uint32_t req_cmd; /* Request command code */ + int rsp_size; /* Response size */ + VbError_t retval; /* Value to return */ +}; + +#define MAXCALLS 8 +static struct srcall calls[MAXCALLS]; +static int ncalls; + +/** + * Reset mock data (for use before each test) + */ +static void ResetMocks(void) +{ + int i; + + *debug_info = 0; + mock_retval = VBERROR_SUCCESS; + + memset(calls, 0, sizeof(calls)); + for (i = 0; i < MAXCALLS; i++) + calls[i].rsp = calls[i].rsp_buf; + ncalls = 0; +} + +/** + * Set response code and length for call <call_idx>. + */ +static void SetResponse(int call_idx, uint32_t response_code, int rsp_size) +{ + struct srcall *c = calls + call_idx; + + c->rsp_size = rsp_size; + ToTpmUint32(c->rsp_buf + 6, response_code); +} + +/* Mocks */ + +VbError_t VbExTpmInit(void) +{ + return mock_retval; +} + + +VbError_t VbExTpmClose(void) +{ + return mock_retval; +} + +VbError_t VbExTpmSendReceive(const uint8_t *request, uint32_t request_length, + uint8_t *response, uint32_t *response_length) +{ + struct srcall *c = calls + ncalls++; + + c->req = request; + c->req_size = request_length; + + /* Parse out the command code */ + FromTpmUint32(request + 6, &c->req_cmd); + + // KLUDGE - remove + printf("TSR [%d] 0x%x\n", ncalls-1, c->req_cmd); + + memset(response, 0, *response_length); + if (c->rsp_size) + memcpy(response, c->rsp, c->rsp_size); + *response_length = c->rsp_size; + + return c->retval; +} + +/** + * Test assorted tlcl functions + */ +static void TlclTest(void) +{ + uint8_t buf[32], buf2[32]; + + ResetMocks(); + TEST_EQ(TlclLibInit(), VBERROR_SUCCESS, "Init"); + + ResetMocks(); + mock_retval = VBERROR_SIMULATED; + TEST_EQ(TlclLibInit(), mock_retval, "Init bad"); + + ResetMocks(); + TEST_EQ(TlclLibClose(), VBERROR_SUCCESS, "Close"); + + ResetMocks(); + mock_retval = VBERROR_SIMULATED; + TEST_EQ(TlclLibClose(), mock_retval, "Close bad"); + + ResetMocks(); + ToTpmUint32(buf + 2, 123); + TEST_EQ(TlclPacketSize(buf), 123, "TlclPacketSize"); + + ResetMocks(); + ToTpmUint32(buf + 2, 10); + TEST_EQ(TlclSendReceive(buf, buf2, sizeof(buf2)), 0, "SendReceive"); + TEST_PTR_EQ(calls[0].req, buf, "SendReceive req ptr"); + TEST_EQ(calls[0].req_size, 10, "SendReceive size"); + + ResetMocks(); + calls[0].retval = VBERROR_SIMULATED; + ToTpmUint32(buf + 2, 10); + TEST_EQ(TlclSendReceive(buf, buf2, sizeof(buf2)), VBERROR_SIMULATED, + "SendReceive fail"); + + ResetMocks(); + SetResponse(0, 123, 10); + ToTpmUint32(buf + 2, 10); + TEST_EQ(TlclSendReceive(buf, buf2, sizeof(buf2)), 123, + "SendReceive error response"); + + // TODO: continue self test (if needed or doing) + // TODO: then retry doing self test + +} + + +/** + * Test send-command functions + */ +static void SendCommandTest(void) +{ + ResetMocks(); + TEST_EQ(TlclStartup(), 0, "SaveState"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_Startup, " cmd"); + + ResetMocks(); + TEST_EQ(TlclSaveState(), 0, "SaveState"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_SaveState, " cmd"); + + ResetMocks(); + TEST_EQ(TlclResume(), 0, "Resume"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_Startup, " cmd"); + + ResetMocks(); + TEST_EQ(TlclSelfTestFull(), 0, "SelfTestFull"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_SelfTestFull, " cmd"); + + ResetMocks(); + TEST_EQ(TlclContinueSelfTest(), 0, "ContinueSelfTest"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_ContinueSelfTest, " cmd"); + + ResetMocks(); + TEST_EQ(TlclAssertPhysicalPresence(), 0, + "AssertPhysicalPresence"); + TEST_EQ(calls[0].req_cmd, TSC_ORD_PhysicalPresence, " cmd"); + + ResetMocks(); + TEST_EQ(TlclPhysicalPresenceCMDEnable(), 0, + "PhysicalPresenceCMDEnable"); + TEST_EQ(calls[0].req_cmd, TSC_ORD_PhysicalPresence, " cmd"); + + ResetMocks(); + TEST_EQ(TlclFinalizePhysicalPresence(), 0, + "FinalizePhysicalPresence"); + TEST_EQ(calls[0].req_cmd, TSC_ORD_PhysicalPresence, " cmd"); + + ResetMocks(); + TEST_EQ(TlclAssertPhysicalPresenceResult(), 0, + "AssertPhysicalPresenceResult"); + TEST_EQ(calls[0].req_cmd, TSC_ORD_PhysicalPresence, " cmd"); + + ResetMocks(); + TEST_EQ(TlclLockPhysicalPresence(), 0, + "LockPhysicalPresence"); + TEST_EQ(calls[0].req_cmd, TSC_ORD_PhysicalPresence, " cmd"); + + ResetMocks(); + TEST_EQ(TlclIsOwned(), 0, "IsOwned"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_ReadPubek, " cmd"); + ResetMocks(); + calls[0].retval = VBERROR_SIMULATED; + TEST_NEQ(TlclIsOwned(), 0, "IsOwned"); + + ResetMocks(); + TEST_EQ(TlclForceClear(), 0, "ForceClear"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_ForceClear, " cmd"); + + ResetMocks(); + TEST_EQ(TlclSetEnable(), 0, "SetEnable"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_PhysicalEnable, " cmd"); + + ResetMocks(); + TEST_EQ(TlclClearEnable(), 0, "ClearEnable"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_PhysicalDisable, " cmd"); + + ResetMocks(); + TEST_EQ(TlclSetDeactivated(0), 0, "SetDeactivated"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_PhysicalSetDeactivated, " cmd"); +} + +/** + * NV spaces test + * + * TODO: check params/data read/written. + */ +static void ReadWriteTest(void) +{ + uint8_t buf[32]; + + ResetMocks(); + TEST_EQ(TlclDefineSpace(1, 2, 3), 0, "DefineSpace"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_NV_DefineSpace, " cmd"); + + ResetMocks(); + TEST_EQ(TlclSetNvLocked(), 0, "SetNvLocked"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_NV_DefineSpace, " cmd"); + + ResetMocks(); + TEST_EQ(TlclWrite(1, buf, 3), 0, "Write"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_NV_WriteValue, " cmd"); + + ResetMocks(); + TEST_EQ(TlclRead(1, buf, 3), 0, "Read"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_NV_ReadValue, " cmd"); + + ResetMocks(); + TEST_EQ(TlclWriteLock(1), 0, "WriteLock"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_NV_WriteValue, " cmd"); + + ResetMocks(); + TEST_EQ(TlclReadLock(1), 0, "ReadLock"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_NV_ReadValue, " cmd"); + + ResetMocks(); + TEST_EQ(TlclSetGlobalLock(), 0, "SetGlobalLock"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_NV_WriteValue, " cmd"); +} + +/** + * Test PCR funcs + * + * TODO: check params/data read/written. + */ +static void PcrTest(void) +{ + uint8_t buf[kPcrDigestLength], buf2[kPcrDigestLength]; + + ResetMocks(); + TEST_EQ(TlclPCRRead(1, buf, kPcrDigestLength), 0, "PCRRead"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_PcrRead, " cmd"); + + ResetMocks(); + TEST_EQ(TlclPCRRead(1, buf, kPcrDigestLength - 1), TPM_E_IOERROR, + "PCRRead too small"); + + ResetMocks(); + TEST_EQ(TlclExtend(1, buf, buf2), 0, "Extend"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_Extend, " cmd"); +} + +/** + * Test flags / capabilities + * + * TODO: check params/data read/written. + */ +static void FlagsTest(void) +{ + TPM_PERMANENT_FLAGS pflags; + TPM_STCLEAR_FLAGS vflags; + uint8_t disable = 0, deactivated = 0, nvlocked = 0; + uint32_t u; + uint8_t buf[32]; + + ResetMocks(); + TEST_EQ(TlclGetPermanentFlags(&pflags), 0, "GetPermanentFlags"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_GetCapability, " cmd"); + + ResetMocks(); + TEST_EQ(TlclGetSTClearFlags(&vflags), 0, "GetSTClearFlags"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_GetCapability, " cmd"); + + ResetMocks(); + TEST_EQ(TlclGetFlags(NULL, NULL, NULL), 0, "GetFlags NULL"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_GetCapability, " cmd"); + ResetMocks(); + TEST_EQ(TlclGetFlags(&disable, &deactivated, &nvlocked), 0, "GetFlags"); + + ResetMocks(); + TEST_EQ(TlclGetPermissions(1, &u), 0, "GetPermissions"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_GetCapability, " cmd"); + + ResetMocks(); + TEST_EQ(TlclGetOwnership(buf), 0, "GetOwnership"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_GetCapability, " cmd"); +} + +/** + * Test random + * + * TODO: check params/data read/written. + * TODO: check overflow tests. + */ +static void RandomTest(void) +{ + uint8_t buf[32]; + uint32_t size; + + ResetMocks(); + size = sizeof(buf); + TEST_EQ(TlclGetRandom(buf, sizeof(buf), &size), 0, "GetRandom"); + TEST_EQ(calls[0].req_cmd, TPM_ORD_GetRandom, " cmd"); + TEST_EQ(size, 0, " size 0"); +} + +int main(void) +{ + TlclTest(); + SendCommandTest(); + ReadWriteTest(); + PcrTest(); + FlagsTest(); + RandomTest(); + + return gTestSuccess ? 0 : 255; +} diff --git a/tests/vboot_api_kernel2_tests.c b/tests/vboot_api_kernel2_tests.c new file mode 100644 index 00000000..9b921cd2 --- /dev/null +++ b/tests/vboot_api_kernel2_tests.c @@ -0,0 +1,554 @@ +/* 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 vboot_api_kernel, part 2 + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "gbb_header.h" +#include "host_common.h" +#include "load_kernel_fw.h" +#include "rollback_index.h" +#include "test_common.h" +#include "vboot_audio.h" +#include "vboot_common.h" +#include "vboot_kernel.h" +#include "vboot_nvstorage.h" +#include "vboot_struct.h" + +/* Mock data */ +static VbCommonParams cparams; +static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE]; +static VbSharedDataHeader *shared = (VbSharedDataHeader *)shared_data; +static GoogleBinaryBlockHeader gbb; +static LoadKernelParams lkp; + +static int shutdown_request_calls_left; +static int audio_looping_calls_left; +static uint32_t vbtlk_retval; +static int vbexlegacy_called; +static int trust_ec; +static int virtdev_set; +static uint32_t virtdev_retval; + +static uint32_t mock_keypress[8]; +static uint32_t mock_keypress_count; +static uint32_t screens_displayed[8]; +static uint32_t screens_count = 0; +static uint32_t mock_num_disks[8]; +static uint32_t mock_num_disks_count; + +/* 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; + + /* + * Only the outermost vboot_api_kernel call sets vboot_api_kernel's + * vnc. So clear it here too. + */ + Memset(VbApiKernelGetVnc(), 0, sizeof(VbNvContext)); + VbNvSetup(VbApiKernelGetVnc()); + VbNvTeardown(VbApiKernelGetVnc()); /* So CRC gets generated */ + + Memset(&shared_data, 0, sizeof(shared_data)); + VbSharedDataInit(shared, sizeof(shared_data)); + + Memset(&lkp, 0, sizeof(lkp)); + + shutdown_request_calls_left = -1; + audio_looping_calls_left = 30; + vbtlk_retval = 1000; + vbexlegacy_called = 0; + trust_ec = 0; + virtdev_set = 0; + virtdev_retval = 0; + + Memset(screens_displayed, 0, sizeof(screens_displayed)); + screens_count = 0; + + Memset(mock_keypress, 0, sizeof(mock_keypress)); + mock_keypress_count = 0; + + Memset(mock_num_disks, 0, sizeof(mock_num_disks)); + mock_num_disks_count = 0; +} + +/* Mock functions */ + +uint32_t VbExIsShutdownRequested(void) +{ + if (shutdown_request_calls_left == 0) + return 1; + else if (shutdown_request_calls_left > 0) + shutdown_request_calls_left--; + + return 0; +} + +uint32_t VbExKeyboardRead(void) +{ + if (mock_keypress_count < ARRAY_SIZE(mock_keypress)) + return mock_keypress[mock_keypress_count++]; + else + return 0; +} + +int VbExLegacy(void) +{ + vbexlegacy_called++; + return 0; +} + +VbError_t VbExDiskGetInfo(VbDiskInfo **infos_ptr, uint32_t *count, + uint32_t disk_flags) +{ + if (mock_num_disks_count < ARRAY_SIZE(mock_num_disks)) { + if (mock_num_disks[mock_num_disks_count] == -1) + return VBERROR_SIMULATED; + else + *count = mock_num_disks[mock_num_disks_count++]; + } else { + *count = 0; + } + return VBERROR_SUCCESS; +} + +VbError_t VbExDiskFreeInfo(VbDiskInfo *infos, + VbExDiskHandle_t preserve_handle) +{ + return VBERROR_SUCCESS; +} + +int VbExTrustEC(void) +{ + return trust_ec; +} + +int VbAudioLooping(VbAudioContext *audio) +{ + if (audio_looping_calls_left == 0) + return 0; + else if (audio_looping_calls_left > 0) + audio_looping_calls_left--; + + return 1; +} + +uint32_t VbTryLoadKernel(VbCommonParams *cparams, LoadKernelParams *p, + uint32_t get_info_flags) +{ + return vbtlk_retval + get_info_flags; +} + +VbError_t VbDisplayScreen(VbCommonParams *cparams, uint32_t screen, int force, + VbNvContext *vncptr) +{ + if (screens_count < ARRAY_SIZE(screens_displayed)) + screens_displayed[screens_count++] = screen; + + return VBERROR_SUCCESS; +} + +uint32_t SetVirtualDevMode(int val) +{ + virtdev_set = val; + return virtdev_retval; +} + +/* Tests */ + +static void VbUserConfirmsTest(void) +{ + printf("Testing VbUserConfirms()...\n"); + + ResetMocks(); + shutdown_request_calls_left = 1; + TEST_EQ(VbUserConfirms(&cparams, 0), -1, "Shutdown requested"); + + ResetMocks(); + mock_keypress[0] = '\r'; + TEST_EQ(VbUserConfirms(&cparams, 0), 1, "Enter"); + + ResetMocks(); + mock_keypress[0] = 0x1b; + TEST_EQ(VbUserConfirms(&cparams, 0), 0, "Esc"); + + ResetMocks(); + mock_keypress[0] = ' '; + shutdown_request_calls_left = 1; + TEST_EQ(VbUserConfirms(&cparams, 1), 0, "Space means no"); + + ResetMocks(); + mock_keypress[0] = ' '; + shutdown_request_calls_left = 1; + TEST_EQ(VbUserConfirms(&cparams, 0), -1, "Space ignored"); + + printf("...done.\n"); +} + +static void VbBootTest(void) +{ + ResetMocks(); + TEST_EQ(VbBootNormal(&cparams, &lkp), 1002, "VbBootNormal()"); +} + +static void VbBootDevTest(void) +{ + uint32_t u; + + printf("Testing VbBootDeveloper()...\n"); + + /* Proceed after timeout */ + ResetMocks(); + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Timeout"); + TEST_EQ(screens_displayed[0], VB_SCREEN_DEVELOPER_WARNING, + " warning screen"); + VbNvGet(VbApiKernelGetVnc(), VBNV_RECOVERY_REQUEST, &u); + TEST_EQ(u, 0, " recovery reason"); + TEST_EQ(audio_looping_calls_left, 0, " used up audio"); + + /* Up arrow is uninteresting / passed to VbCheckDisplayKey() */ + ResetMocks(); + mock_keypress[0] = VB_KEY_UP; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Up arrow"); + + /* Shutdown requested in loop */ + ResetMocks(); + shutdown_request_calls_left = 2; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Shutdown requested"); + TEST_NEQ(audio_looping_calls_left, 0, " aborts audio"); + + /* Space goes straight to recovery if no virtual dev switch */ + ResetMocks(); + mock_keypress[0] = ' '; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), VBERROR_LOAD_KERNEL_RECOVERY, + "Space = recovery"); + VbNvGet(VbApiKernelGetVnc(), VBNV_RECOVERY_REQUEST, &u); + TEST_EQ(u, VBNV_RECOVERY_RW_DEV_SCREEN, " recovery reason"); + + /* Space asks to disable virtual dev switch */ + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_DEV_SWITCH_ON; + mock_keypress[0] = ' '; + mock_keypress[1] = '\r'; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), VBERROR_TPM_REBOOT_REQUIRED, + "Space = tonorm"); + TEST_EQ(screens_displayed[0], VB_SCREEN_DEVELOPER_WARNING, + " warning screen"); + TEST_EQ(screens_displayed[1], VB_SCREEN_DEVELOPER_TO_NORM, + " tonorm screen"); + TEST_EQ(screens_displayed[2], VB_SCREEN_TO_NORM_CONFIRMED, + " confirm screen"); + VbNvGet(VbApiKernelGetVnc(), VBNV_DISABLE_DEV_REQUEST, &u); + TEST_EQ(u, 1, " disable dev request"); + + /* Space-space doesn't disable it */ + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_DEV_SWITCH_ON; + mock_keypress[0] = ' '; + mock_keypress[1] = ' '; + mock_keypress[2] = 0x1b; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Space-space"); + TEST_EQ(screens_displayed[0], VB_SCREEN_DEVELOPER_WARNING, + " warning screen"); + TEST_EQ(screens_displayed[1], VB_SCREEN_DEVELOPER_TO_NORM, + " tonorm screen"); + TEST_EQ(screens_displayed[2], VB_SCREEN_DEVELOPER_WARNING, + " warning screen"); + + /* Enter doesn't by default */ + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_DEV_SWITCH_ON; + mock_keypress[0] = '\r'; + mock_keypress[1] = '\r'; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Enter ignored"); + + /* Enter does if GBB flag set */ + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_DEV_SWITCH_ON; + gbb.flags |= GBB_FLAG_ENTER_TRIGGERS_TONORM; + mock_keypress[0] = '\r'; + mock_keypress[1] = '\r'; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), VBERROR_TPM_REBOOT_REQUIRED, + "Enter = tonorm"); + + /* Tonorm ignored if GBB forces dev switch on */ + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_DEV_SWITCH_ON; + gbb.flags |= GBB_FLAG_FORCE_DEV_SWITCH_ON; + mock_keypress[0] = ' '; + mock_keypress[1] = '\r'; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Can't tonorm gbb-dev"); + + /* Shutdown requested at tonorm screen */ + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_DEV_SWITCH_ON; + mock_keypress[0] = ' '; + shutdown_request_calls_left = 2; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Shutdown requested at tonorm"); + TEST_EQ(screens_displayed[0], VB_SCREEN_DEVELOPER_WARNING, + " warning screen"); + TEST_EQ(screens_displayed[1], VB_SCREEN_DEVELOPER_TO_NORM, + " tonorm screen"); + + /* Ctrl+D dismisses warning */ + ResetMocks(); + mock_keypress[0] = 0x04; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+D"); + VbNvGet(VbApiKernelGetVnc(), VBNV_RECOVERY_REQUEST, &u); + TEST_EQ(u, 0, " recovery reason"); + TEST_NEQ(audio_looping_calls_left, 0, " aborts audio"); + + /* Ctrl+L tries legacy boot mode only if enabled */ + ResetMocks(); + mock_keypress[0] = 0x0c; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+L normal"); + TEST_EQ(vbexlegacy_called, 0, " not legacy"); + + ResetMocks(); + + gbb.flags |= GBB_FLAG_FORCE_DEV_BOOT_LEGACY; + mock_keypress[0] = 0x0c; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+L force legacy"); + TEST_EQ(vbexlegacy_called, 1, " try legacy"); + + ResetMocks(); + VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_BOOT_LEGACY, 1); + mock_keypress[0] = 0x0c; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+L nv legacy"); + TEST_EQ(vbexlegacy_called, 1, " try legacy"); + + /* Ctrl+U boots USB only if enabled */ + ResetMocks(); + mock_keypress[0] = 0x15; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+U normal"); + + /* Ctrl+U enabled, with good USB boot */ + ResetMocks(); + VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_BOOT_USB, 1); + mock_keypress[0] = 0x15; + vbtlk_retval = VBERROR_SUCCESS - VB_DISK_FLAG_REMOVABLE; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 0, "Ctrl+U USB"); + + /* Ctrl+U enabled via GBB */ + ResetMocks(); + gbb.flags |= GBB_FLAG_FORCE_DEV_BOOT_USB; + mock_keypress[0] = 0x15; + vbtlk_retval = VBERROR_SUCCESS - VB_DISK_FLAG_REMOVABLE; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 0, "Ctrl+U force USB"); + + /* If no USB, eventually times out and tries fixed disk */ + ResetMocks(); + VbNvSet(VbApiKernelGetVnc(), VBNV_DEV_BOOT_USB, 1); + mock_keypress[0] = 0x15; + TEST_EQ(VbBootDeveloper(&cparams, &lkp), 1002, "Ctrl+U enabled"); + TEST_EQ(vbexlegacy_called, 0, " not legacy"); + VbNvGet(VbApiKernelGetVnc(), VBNV_RECOVERY_REQUEST, &u); + TEST_EQ(u, 0, " recovery reason"); + TEST_EQ(audio_looping_calls_left, 0, " used up audio"); + + printf("...done.\n"); +} + +static void VbBootRecTest(void) +{ + uint32_t u; + + printf("Testing VbBootRecovery()...\n"); + + /* Shutdown requested in loop */ + ResetMocks(); + shutdown_request_calls_left = 10; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Shutdown requested"); + VbNvGet(VbApiKernelGetVnc(), VBNV_RECOVERY_REQUEST, &u); + TEST_EQ(u, 0, " recovery reason"); + TEST_EQ(screens_displayed[0], VB_SCREEN_BLANK, + " blank screen"); + TEST_EQ(screens_displayed[1], VB_SCREEN_RECOVERY_NO_GOOD, + " no good screen"); + + /* Disk inserted after start */ + ResetMocks(); + vbtlk_retval = VBERROR_SUCCESS - VB_DISK_FLAG_REMOVABLE; + TEST_EQ(VbBootRecovery(&cparams, &lkp), 0, "Good"); + + /* No disk inserted */ + ResetMocks(); + vbtlk_retval = VBERROR_NO_DISK_FOUND - VB_DISK_FLAG_REMOVABLE; + shutdown_request_calls_left = 10; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Bad disk"); + TEST_EQ(screens_displayed[0], VB_SCREEN_BLANK, + " blank screen"); + TEST_EQ(screens_displayed[1], VB_SCREEN_RECOVERY_INSERT, + " insert screen"); + + /* Remove disks */ + ResetMocks(); + shutdown_request_calls_left = 100; + mock_num_disks[0] = 1; + mock_num_disks[1] = 1; + vbtlk_retval = VBERROR_NO_DISK_FOUND - VB_DISK_FLAG_REMOVABLE; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Remove"); + TEST_EQ(screens_displayed[0], VB_SCREEN_RECOVERY_REMOVE, + " remove screen"); + TEST_EQ(screens_displayed[1], VB_SCREEN_RECOVERY_REMOVE, + " remove screen"); + TEST_EQ(screens_displayed[2], VB_SCREEN_BLANK, + " blank screen"); + TEST_EQ(screens_displayed[3], VB_SCREEN_RECOVERY_INSERT, + " insert screen"); + + /* No removal if dev switch is on */ + ResetMocks(); + shutdown_request_calls_left = 100; + mock_num_disks[0] = 1; + mock_num_disks[1] = 1; + shared->flags |= VBSD_BOOT_DEV_SWITCH_ON; + vbtlk_retval = VBERROR_NO_DISK_FOUND - VB_DISK_FLAG_REMOVABLE; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "No remove in dev"); + TEST_EQ(screens_displayed[0], VB_SCREEN_RECOVERY_INSERT, + " insert screen"); + + /* No removal if recovery button physically pressed */ + ResetMocks(); + shutdown_request_calls_left = 100; + mock_num_disks[0] = 1; + mock_num_disks[1] = 1; + shared->flags |= VBSD_BOOT_REC_SWITCH_ON; + vbtlk_retval = VBERROR_NO_DISK_FOUND - VB_DISK_FLAG_REMOVABLE; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "No remove in rec"); + TEST_EQ(screens_displayed[0], VB_SCREEN_RECOVERY_INSERT, + " insert screen"); + + /* Bad disk count doesn't require removal */ + ResetMocks(); + mock_num_disks[0] = -1; + vbtlk_retval = VBERROR_NO_DISK_FOUND - VB_DISK_FLAG_REMOVABLE; + shutdown_request_calls_left = 10; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Bad disk count"); + TEST_EQ(screens_displayed[0], VB_SCREEN_BLANK, + " blank screen"); + TEST_EQ(screens_displayed[1], VB_SCREEN_RECOVERY_INSERT, + " insert screen"); + + /* Ctrl+D ignored for many reasons... */ + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_REC_SWITCH_ON; + shutdown_request_calls_left = 100; + mock_keypress[0] = 0x04; + trust_ec = 0; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Ctrl+D ignored if EC not trusted"); + TEST_EQ(virtdev_set, 0, " virtual dev mode off"); + TEST_NEQ(screens_displayed[1], VB_SCREEN_RECOVERY_TO_DEV, + " todev screen"); + + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_REC_SWITCH_ON | + VBSD_BOOT_DEV_SWITCH_ON; + trust_ec = 1; + shutdown_request_calls_left = 100; + mock_keypress[0] = 0x04; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Ctrl+D ignored if already in dev mode"); + TEST_EQ(virtdev_set, 0, " virtual dev mode off"); + TEST_NEQ(screens_displayed[1], VB_SCREEN_RECOVERY_TO_DEV, + " todev screen"); + + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH; + trust_ec = 1; + shutdown_request_calls_left = 100; + mock_keypress[0] = 0x04; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Ctrl+D ignored if recovery not manually triggered"); + TEST_EQ(virtdev_set, 0, " virtual dev mode off"); + TEST_NEQ(screens_displayed[1], VB_SCREEN_RECOVERY_TO_DEV, + " todev screen"); + + ResetMocks(); + shared->flags = VBSD_BOOT_REC_SWITCH_ON; + trust_ec = 1; + shutdown_request_calls_left = 100; + mock_keypress[0] = 0x04; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Ctrl+D ignored if no virtual dev switch"); + TEST_EQ(virtdev_set, 0, " virtual dev mode off"); + TEST_NEQ(screens_displayed[1], VB_SCREEN_RECOVERY_TO_DEV, + " todev screen"); + + /* Ctrl+D then space means don't enable */ + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_REC_SWITCH_ON; + shutdown_request_calls_left = 100; + vbtlk_retval = VBERROR_NO_DISK_FOUND - VB_DISK_FLAG_REMOVABLE; + trust_ec = 1; + mock_keypress[0] = 0x04; + mock_keypress[1] = ' '; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_SHUTDOWN_REQUESTED, + "Ctrl+D todev abort"); + TEST_EQ(screens_displayed[0], VB_SCREEN_RECOVERY_INSERT, + " insert screen"); + TEST_EQ(screens_displayed[1], VB_SCREEN_RECOVERY_TO_DEV, + " todev screen"); + TEST_EQ(screens_displayed[2], VB_SCREEN_RECOVERY_INSERT, + " insert screen"); + TEST_EQ(virtdev_set, 0, " virtual dev mode off"); + + /* Ctrl+D then enter means enable */ + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_REC_SWITCH_ON; + shutdown_request_calls_left = 100; + vbtlk_retval = VBERROR_NO_DISK_FOUND - VB_DISK_FLAG_REMOVABLE; + trust_ec = 1; + mock_keypress[0] = 0x04; + mock_keypress[1] = '\r'; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_TPM_REBOOT_REQUIRED, + "Ctrl+D todev confirm"); + TEST_EQ(virtdev_set, 1, " virtual dev mode on"); + + /* Handle TPM error in enabling dev mode */ + ResetMocks(); + shared->flags = VBSD_HONOR_VIRT_DEV_SWITCH | VBSD_BOOT_REC_SWITCH_ON; + shutdown_request_calls_left = 100; + vbtlk_retval = VBERROR_NO_DISK_FOUND - VB_DISK_FLAG_REMOVABLE; + trust_ec = 1; + mock_keypress[0] = 0x04; + mock_keypress[1] = '\r'; + virtdev_retval = VBERROR_SIMULATED; + TEST_EQ(VbBootRecovery(&cparams, &lkp), VBERROR_TPM_SET_BOOT_MODE_STATE, + "Ctrl+D todev failure"); + + printf("...done.\n"); +} + + +int main(void) +{ + VbUserConfirmsTest(); + VbBootTest(); + VbBootDevTest(); + VbBootRecTest(); + + return gTestSuccess ? 0 : 255; +} diff --git a/tests/vboot_api_kernel3_tests.c b/tests/vboot_api_kernel3_tests.c new file mode 100644 index 00000000..9ec6f19b --- /dev/null +++ b/tests/vboot_api_kernel3_tests.c @@ -0,0 +1,362 @@ +/* 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 vboot_api_kernel, part 3 - software sync + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "gbb_header.h" +#include "host_common.h" +#include "load_kernel_fw.h" +#include "rollback_index.h" +#include "test_common.h" +#include "vboot_audio.h" +#include "vboot_common.h" +#include "vboot_kernel.h" +#include "vboot_nvstorage.h" +#include "vboot_struct.h" + +/* Mock data */ +static VbCommonParams cparams; +static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE]; +static VbSharedDataHeader *shared = (VbSharedDataHeader *)shared_data; +static GoogleBinaryBlockHeader gbb; + +static int trust_ec; +static int mock_in_rw; +static VbError_t in_rw_retval; +static int protect_retval; +static int ec_protected; +static int run_retval; +static int ec_run_image; +static int update_retval; +static int ec_updated; +static int get_expected_retval; +static int shutdown_request_calls_left; + +static uint8_t mock_ec_hash[32]; +static int mock_ec_hash_size; +static uint8_t want_ec_hash[32]; +static int want_ec_hash_size; +static uint8_t mock_sha[32]; + +static uint32_t screens_displayed[8]; +static uint32_t screens_count = 0; + +/* 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; + + /* + * Only the outermost vboot_api_kernel call sets vboot_api_kernel's + * vnc. So clear it here too. + */ + Memset(VbApiKernelGetVnc(), 0, sizeof(VbNvContext)); + VbNvSetup(VbApiKernelGetVnc()); + VbNvTeardown(VbApiKernelGetVnc()); /* So CRC gets generated */ + + Memset(&shared_data, 0, sizeof(shared_data)); + VbSharedDataInit(shared, sizeof(shared_data)); + + trust_ec = 0; + mock_in_rw = 0; + ec_protected = 0; + ec_run_image = -1; /* 0 = RO, 1 = RW */ + ec_updated = 0; + in_rw_retval = VBERROR_SUCCESS; + protect_retval = VBERROR_SUCCESS; + update_retval = VBERROR_SUCCESS; + run_retval = VBERROR_SUCCESS; + get_expected_retval = VBERROR_SUCCESS; + shutdown_request_calls_left = -1; + + Memset(mock_ec_hash, 0, sizeof(mock_ec_hash)); + mock_ec_hash[0] = 42; + mock_ec_hash_size = sizeof(mock_ec_hash); + + Memset(want_ec_hash, 0, sizeof(want_ec_hash)); + want_ec_hash[0] = 42; + want_ec_hash_size = sizeof(want_ec_hash); + + Memset(mock_sha, 0, sizeof(want_ec_hash)); + mock_sha[0] = 42; + + // TODO: ensure these are actually needed + + Memset(screens_displayed, 0, sizeof(screens_displayed)); + screens_count = 0; +} + +/* Mock functions */ + +uint32_t VbExIsShutdownRequested(void) +{ + if (shutdown_request_calls_left == 0) + return 1; + else if (shutdown_request_calls_left > 0) + shutdown_request_calls_left--; + + return 0; +} + +int VbExTrustEC(void) +{ + return trust_ec; +} + +VbError_t VbExEcRunningRW(int *in_rw) +{ + *in_rw = mock_in_rw; + return in_rw_retval; +} + +VbError_t VbExEcProtectRW(void) +{ + ec_protected = 1; + return protect_retval; +} + +VbError_t VbExEcStayInRO(void) +{ + ec_run_image = 0; + return run_retval; +} + +VbError_t VbExEcJumpToRW(void) +{ + ec_run_image = 1; + return run_retval; +} + +VbError_t VbExEcHashRW(const uint8_t **hash, int *hash_size) +{ + *hash = mock_ec_hash; + *hash_size = mock_ec_hash_size; + return mock_ec_hash_size ? VBERROR_SUCCESS : VBERROR_SIMULATED; +} + +VbError_t VbExEcGetExpectedRW(enum VbSelectFirmware_t select, + const uint8_t **image, int *image_size) +{ + static uint8_t fake_image[64] = {5, 6, 7, 8}; + *image = fake_image; + *image_size = sizeof(fake_image); + return get_expected_retval; +} + +VbError_t VbExEcGetExpectedRWHash(enum VbSelectFirmware_t select, + const uint8_t **hash, int *hash_size) +{ + *hash = want_ec_hash; + *hash_size = want_ec_hash_size; + + if (want_ec_hash_size == -1) + return VBERROR_EC_GET_EXPECTED_HASH_FROM_IMAGE; + else + return want_ec_hash_size ? VBERROR_SUCCESS : VBERROR_SIMULATED; +} + +uint8_t *internal_SHA256(const uint8_t *data, uint64_t len, uint8_t *digest) +{ + Memcpy(digest, mock_sha, sizeof(mock_sha)); + return digest; +} + +VbError_t VbExEcUpdateRW(const uint8_t *image, int image_size) +{ + ec_updated = 1; + return update_retval; +} + +VbError_t VbDisplayScreen(VbCommonParams *cparams, uint32_t screen, int force, + VbNvContext *vncptr) +{ + if (screens_count < ARRAY_SIZE(screens_displayed)) + screens_displayed[screens_count++] = screen; + + return VBERROR_SUCCESS; +} + +static void test_ssync(VbError_t retval, int recovery_reason, const char *desc) +{ + uint32_t u; + + TEST_EQ(VbEcSoftwareSync(&cparams), retval, desc); + VbNvGet(VbApiKernelGetVnc(), VBNV_RECOVERY_REQUEST, &u); + TEST_EQ(u, recovery_reason, " recovery reason"); +} + +/* Tests */ + +static void VbSoftwareSyncTest(void) +{ + /* Recovery cases */ + ResetMocks(); + shared->recovery_reason = 123; + test_ssync(0, 0, "In recovery, EC-RO"); + TEST_EQ(ec_protected, 0, " ec protected"); + + ResetMocks(); + shared->recovery_reason = 123; + mock_in_rw = 1; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + 123, "Recovery needs EC-RO"); + + /* AP-RO cases */ + ResetMocks(); + in_rw_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_UNKNOWN_IMAGE, "Unknown EC image"); + + ResetMocks(); + shared->flags |= VBSD_LF_USE_RO_NORMAL; + mock_in_rw = 1; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + 0, "AP-RO needs EC-RO"); + + ResetMocks(); + shared->flags |= VBSD_LF_USE_RO_NORMAL; + test_ssync(0, 0, "AP-RO, EC-RO"); + TEST_EQ(ec_protected, 1, " ec protected"); + TEST_EQ(ec_run_image, 0, " ec run image"); + + ResetMocks(); + shared->flags |= VBSD_LF_USE_RO_NORMAL; + run_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_SOFTWARE_SYNC, "Stay in RO fail"); + + ResetMocks(); + shared->flags |= VBSD_LF_USE_RO_NORMAL; + protect_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_SIMULATED, + VBNV_RECOVERY_EC_PROTECT, "Protect error"); + + ResetMocks(); + shared->flags |= VBSD_LF_USE_RO_NORMAL; + shutdown_request_calls_left = 0; + test_ssync(VBERROR_SHUTDOWN_REQUESTED, 0, "AP-RO shutdown requested"); + + /* Calculate hashes */ + ResetMocks(); + mock_ec_hash_size = 0; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_HASH_FAILED, "Bad EC hash"); + + ResetMocks(); + mock_ec_hash_size = 16; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_HASH_SIZE, "Bad EC hash size"); + + ResetMocks(); + want_ec_hash_size = 0; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_EXPECTED_HASH, "Bad precalculated hash"); + + ResetMocks(); + want_ec_hash_size = 16; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_EXPECTED_HASH, + "Bad precalculated hash size"); + + ResetMocks(); + mock_in_rw = 1; + want_ec_hash_size = -1; + test_ssync(0, 0, "No precomputed hash"); + + ResetMocks(); + want_ec_hash_size = -1; + get_expected_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_EXPECTED_IMAGE, "Can't fetch image"); + + /* Updates required */ + ResetMocks(); + mock_in_rw = 1; + want_ec_hash[0]++; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_HASH_MISMATCH, + "Precalculated hash mismatch"); + + ResetMocks(); + mock_in_rw = 1; + mock_ec_hash[0]++; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + 0, "Pending update needs reboot"); + + ResetMocks(); + mock_ec_hash[0]++; + test_ssync(0, 0, "Update without reboot"); + TEST_EQ(ec_protected, 1, " ec protected"); + TEST_EQ(ec_run_image, 1, " ec run image"); + TEST_EQ(ec_updated, 1, " ec updated"); + + ResetMocks(); + mock_ec_hash[0]++; + update_retval = VBERROR_EC_REBOOT_TO_RO_REQUIRED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + 0, "Reboot after update"); + TEST_EQ(ec_updated, 1, " ec updated"); + + ResetMocks(); + mock_ec_hash[0]++; + update_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_UPDATE, "Update failed"); + + ResetMocks(); + mock_ec_hash[0]++; + shared->flags |= VBSD_EC_SLOW_UPDATE; + test_ssync(0, 0, "Slow update"); + TEST_EQ(screens_displayed[0], VB_SCREEN_WAIT, " wait screen"); + + /* RW cases, no update */ + ResetMocks(); + mock_in_rw = 1; + test_ssync(0, 0, "AP-RW, EC-RW"); + + ResetMocks(); + test_ssync(0, 0, "AP-RW, EC-RO -> EC-RW"); + TEST_EQ(ec_protected, 1, " ec protected"); + TEST_EQ(ec_run_image, 1, " ec run image"); + TEST_EQ(ec_updated, 0, " ec updated"); + + ResetMocks(); + run_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_EC_REBOOT_TO_RO_REQUIRED, + VBNV_RECOVERY_EC_JUMP_RW, "Jump to RW fail"); + + ResetMocks(); + protect_retval = VBERROR_SIMULATED; + test_ssync(VBERROR_SIMULATED, + VBNV_RECOVERY_EC_PROTECT, "Protect error"); + + ResetMocks(); + shutdown_request_calls_left = 0; + test_ssync(VBERROR_SHUTDOWN_REQUESTED, 0, + "AP-RW, EC-RO -> EC-RW shutdown requested"); + + ResetMocks(); + mock_in_rw = 1; + shutdown_request_calls_left = 0; + test_ssync(VBERROR_SHUTDOWN_REQUESTED, 0, "AP-RW shutdown requested"); +} + +int main(void) +{ + VbSoftwareSyncTest(); + + return gTestSuccess ? 0 : 255; +} diff --git a/tests/vboot_api_kernel4_tests.c b/tests/vboot_api_kernel4_tests.c new file mode 100644 index 00000000..414279e5 --- /dev/null +++ b/tests/vboot_api_kernel4_tests.c @@ -0,0 +1,241 @@ +/* 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 vboot_api_kernel, part 4 - select and load kernel + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "gbb_header.h" +#include "host_common.h" +#include "load_kernel_fw.h" +#include "rollback_index.h" +#include "test_common.h" +#include "vboot_audio.h" +#include "vboot_common.h" +#include "vboot_kernel.h" +#include "vboot_nvstorage.h" +#include "vboot_struct.h" + +/* Mock data */ +static VbCommonParams cparams; +static VbSelectAndLoadKernelParams kparams; +static VbNvContext vnc; +static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE]; +static VbSharedDataHeader *shared = (VbSharedDataHeader *)shared_data; +static GoogleBinaryBlockHeader gbb; + +static int ecsync_retval; +static uint32_t rkr_version; +static uint32_t new_version; +static int rkr_retval, rkw_retval, rkl_retval; +static VbError_t vbboot_retval; + +/* 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(&kparams, 0, sizeof(kparams)); + + 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)); + + ecsync_retval = VBERROR_SUCCESS; + rkr_version = new_version = 0x10002; + rkr_retval = rkw_retval = rkl_retval = VBERROR_SUCCESS; + vbboot_retval = VBERROR_SUCCESS; +} + +/* Mock functions */ + +VbError_t VbExNvStorageRead(uint8_t *buf) +{ + Memcpy(buf, vnc.raw, sizeof(vnc.raw)); + return VBERROR_SUCCESS; +} + +VbError_t VbExNvStorageWrite(const uint8_t *buf) +{ + Memcpy(vnc.raw, buf, sizeof(vnc.raw)); + return VBERROR_SUCCESS; +} + +VbError_t VbEcSoftwareSync(VbCommonParams *cparams) +{ + return ecsync_retval; +} + +uint32_t RollbackKernelRead(uint32_t *version) +{ + *version = rkr_version; + return rkr_retval; +} + +uint32_t RollbackKernelWrite(uint32_t version) +{ + TEST_EQ(version, new_version, "RollbackKernelWrite new version"); + rkr_version = version; + return rkw_retval; +} + +uint32_t RollbackKernelLock(void) +{ + return rkl_retval; +} + +VbError_t VbBootNormal(VbCommonParams *cparams, LoadKernelParams *p) +{ + shared->kernel_version_tpm = new_version; + + if (vbboot_retval == -1) + return VBERROR_SIMULATED; + + return vbboot_retval; +} + +VbError_t VbBootDeveloper(VbCommonParams *cparams, LoadKernelParams *p) +{ + shared->kernel_version_tpm = new_version; + + if (vbboot_retval == -2) + return VBERROR_SIMULATED; + + return vbboot_retval; +} + +VbError_t VbBootRecovery(VbCommonParams *cparams, LoadKernelParams *p) +{ + shared->kernel_version_tpm = new_version; + + if (vbboot_retval == -3) + return VBERROR_SIMULATED; + + return vbboot_retval; +} + +static void test_slk(VbError_t retval, int recovery_reason, const char *desc) +{ + uint32_t u; + + TEST_EQ(VbSelectAndLoadKernel(&cparams, &kparams), retval, desc); + VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &u); + TEST_EQ(u, recovery_reason, " recovery reason"); +} + +/* Tests */ + +static void VbSlkTest(void) +{ + ResetMocks(); + test_slk(0, 0, "Normal"); + + /* Software sync */ + ResetMocks(); + shared->flags |= VBSD_EC_SOFTWARE_SYNC; + ecsync_retval = VBERROR_SIMULATED; + test_slk(VBERROR_SIMULATED, 0, "EC sync bad"); + + ResetMocks(); + ecsync_retval = VBERROR_SIMULATED; + test_slk(0, 0, "EC sync not done"); + + /* Rollback kernel version */ + ResetMocks(); + rkr_retval = 123; + test_slk(VBERROR_TPM_READ_KERNEL, + VBNV_RECOVERY_RW_TPM_R_ERROR, "Read kernel rollback"); + + ResetMocks(); + new_version = 0x20003; + test_slk(0, 0, "Roll forward"); + TEST_EQ(rkr_version, 0x20003, " version"); + + ResetMocks(); + new_version = 0x20003; + shared->flags |= VBSD_FWB_TRIED; + shared->firmware_index = 1; + test_slk(0, 0, "Don't roll forward during try B"); + TEST_EQ(rkr_version, 0x10002, " version"); + + ResetMocks(); + vbboot_retval = VBERROR_INVALID_KERNEL_FOUND; + VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, 123); + VbNvTeardown(&vnc); + shared->flags |= VBSD_FWB_TRIED; + shared->firmware_index = 1; + test_slk(VBERROR_INVALID_KERNEL_FOUND, + 0, "Don't go to recovery if try b fails to find a kernel"); + + ResetMocks(); + new_version = 0x20003; + rkw_retval = 123; + test_slk(VBERROR_TPM_WRITE_KERNEL, + VBNV_RECOVERY_RW_TPM_W_ERROR, "Write kernel rollback"); + + ResetMocks(); + rkl_retval = 123; + test_slk(VBERROR_TPM_LOCK_KERNEL, + VBNV_RECOVERY_RW_TPM_L_ERROR, "Lock kernel rollback"); + + /* Boot normal */ + ResetMocks(); + vbboot_retval = -1; + test_slk(VBERROR_SIMULATED, 0, "Normal boot bad"); + + /* Boot dev */ + ResetMocks(); + shared->flags |= VBSD_BOOT_DEV_SWITCH_ON; + vbboot_retval = -2; + test_slk(VBERROR_SIMULATED, 0, "Dev boot bad"); + + ResetMocks(); + shared->flags |= VBSD_BOOT_DEV_SWITCH_ON; + new_version = 0x20003; + test_slk(0, 0, "Dev doesn't roll forward"); + TEST_EQ(rkr_version, 0x10002, " version"); + + /* Boot recovery */ + ResetMocks(); + shared->recovery_reason = 123; + vbboot_retval = -3; + test_slk(VBERROR_SIMULATED, 0, "Recovery boot bad"); + + ResetMocks(); + shared->recovery_reason = 123; + new_version = 0x20003; + test_slk(0, 0, "Recovery doesn't roll forward"); + TEST_EQ(rkr_version, 0x10002, " version"); + + ResetMocks(); + shared->recovery_reason = 123; + rkr_retval = rkw_retval = rkl_retval = VBERROR_SIMULATED; + test_slk(0, 0, "Recovery ignore TPM errors"); + + + + // todo: rkr/w/l fail ignored if recovery + + +} + +int main(void) +{ + VbSlkTest(); + + return gTestSuccess ? 0 : 255; +} diff --git a/tests/vboot_api_kernel_tests.c b/tests/vboot_api_kernel_tests.c index 444cff7c..8c04814e 100644 --- a/tests/vboot_api_kernel_tests.c +++ b/tests/vboot_api_kernel_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. * @@ -15,33 +15,33 @@ #include "test_common.h" #include "utility.h" #include "vboot_api.h" - +#include "vboot_kernel.h" #define MAX_TEST_DISKS 10 #define DEFAULT_COUNT -1 typedef struct { - uint64_t bytes_per_lba; - uint64_t lba_count; - uint32_t flags; - const char *diskname; + uint64_t bytes_per_lba; + uint64_t lba_count; + uint32_t flags; + const char *diskname; } disk_desc_t; typedef struct { - char *name; + char *name; - /* inputs for test case */ - uint32_t want_flags; - VbError_t diskgetinfo_return_val; - disk_desc_t disks_to_provide[MAX_TEST_DISKS]; - int disk_count_to_return; - VbError_t loadkernel_return_val[MAX_TEST_DISKS]; + /* inputs for test case */ + uint32_t want_flags; + VbError_t diskgetinfo_return_val; + disk_desc_t disks_to_provide[MAX_TEST_DISKS]; + int disk_count_to_return; + VbError_t loadkernel_return_val[MAX_TEST_DISKS]; - /* outputs from test */ - uint32_t expected_recovery_request_val; - const char *expected_to_find_disk; - const char *expected_to_load_disk; - uint32_t expected_return_val; + /* outputs from test */ + uint32_t expected_recovery_request_val; + const char *expected_to_find_disk; + const char *expected_to_load_disk; + uint32_t expected_return_val; } test_case_t; @@ -52,110 +52,124 @@ static const char pickme[] = "correct choice"; #define DONT_CARE ((const char *)42) test_case_t test[] = { - - { - .name = "first removable drive", - .want_flags = VB_DISK_FLAG_REMOVABLE, - .disks_to_provide = { - {512, 10, VB_DISK_FLAG_REMOVABLE, 0 }, /* too small */ - {2048, 100, VB_DISK_FLAG_REMOVABLE, 0 }, /* wrong LBA */ - {512, 100, VB_DISK_FLAG_FIXED, 0 }, /* wrong type */ - {512, 100, 0, 0 }, /* wrong flags */ - {512, 100, -1, 0 }, /* still wrong flags */ - {512, 100, VB_DISK_FLAG_REMOVABLE, pickme}, - {512, 100, VB_DISK_FLAG_REMOVABLE, "holygrail"}, /* already got one */ - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VBERROR_SUCCESS, - .loadkernel_return_val = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, - - .expected_recovery_request_val = VBNV_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = pickme, - .expected_to_load_disk = pickme, - .expected_return_val = VBERROR_SUCCESS - }, - - { - .name = "second removable drive", - .want_flags = VB_DISK_FLAG_REMOVABLE, - .disks_to_provide = { - {512, 100, 0, 0 }, /* wrong flags */ - {512, 100, VB_DISK_FLAG_REMOVABLE, "not yet"}, - {512, 100, VB_DISK_FLAG_REMOVABLE, pickme}, - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VBERROR_SUCCESS, - .loadkernel_return_val = { 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, }, - - .expected_recovery_request_val = VBNV_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = pickme, - .expected_to_load_disk = pickme, - .expected_return_val = VBERROR_SUCCESS - }, - - { - .name = "first fixed drive", - .want_flags = VB_DISK_FLAG_FIXED, - .disks_to_provide = { - {512, 10, VB_DISK_FLAG_FIXED, 0 }, /* too small */ - {2048, 100, VB_DISK_FLAG_FIXED, 0 }, /* wrong LBA */ - {512, 100, VB_DISK_FLAG_REMOVABLE, 0 }, /* wrong type */ - {512, 100, 0, 0 }, /* wrong flags */ - {512, 100, -1, 0 }, /* still wrong flags */ - {512, 100, VB_DISK_FLAG_REMOVABLE|VB_DISK_FLAG_FIXED, 0 }, /* flags */ - {512, 100, VB_DISK_FLAG_FIXED, pickme}, - {512, 100, VB_DISK_FLAG_FIXED, "holygrail"}, /* already got one */ - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VBERROR_SUCCESS, - .loadkernel_return_val = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, - - .expected_recovery_request_val = VBNV_RECOVERY_NOT_REQUESTED, - .expected_to_find_disk = pickme, - .expected_to_load_disk = pickme, - .expected_return_val = VBERROR_SUCCESS - }, - - { - .name = "no drives at all", - .want_flags = VB_DISK_FLAG_FIXED, - .disks_to_provide = { - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VBERROR_SUCCESS, - .loadkernel_return_val = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, - - .expected_recovery_request_val = VBNV_RECOVERY_RW_NO_DISK, - .expected_to_find_disk = 0, - .expected_to_load_disk = 0, - .expected_return_val = VBERROR_NO_DISK_FOUND - }, - - { - .name = "no valid drives", - .want_flags = VB_DISK_FLAG_FIXED, - .disks_to_provide = { - {512, 10, VB_DISK_FLAG_FIXED, 0 }, /* too small */ - {2048, 100, VB_DISK_FLAG_FIXED, 0 }, /* wrong LBA */ - {512, 100, VB_DISK_FLAG_REMOVABLE, 0 }, /* wrong type */ - {512, 100, 0, 0 }, /* wrong flags */ - {512, 100, -1, 0 }, /* still wrong flags */ - {512, 100, VB_DISK_FLAG_FIXED, "bad1"}, /* doesn't load */ - {512, 100, VB_DISK_FLAG_FIXED, "bad2"}, /* doesn't load */ - }, - .disk_count_to_return = DEFAULT_COUNT, - .diskgetinfo_return_val = VBERROR_SUCCESS, - .loadkernel_return_val = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, }, - - .expected_recovery_request_val = VBNV_RECOVERY_RW_NO_DISK, - .expected_to_find_disk = DONT_CARE, - .expected_to_load_disk = 0, - .expected_return_val = 1 - }, - + { + .name = "first removable drive", + .want_flags = VB_DISK_FLAG_REMOVABLE, + .disks_to_provide = { + /* too small */ + {512, 10, VB_DISK_FLAG_REMOVABLE, 0}, + /* wrong LBA */ + {2048, 100, VB_DISK_FLAG_REMOVABLE, 0}, + /* wrong type */ + {512, 100, VB_DISK_FLAG_FIXED, 0}, + /* wrong flags */ + {512, 100, 0, 0}, + /* still wrong flags */ + {512, 100, -1, 0}, + {512, 100, VB_DISK_FLAG_REMOVABLE, pickme}, + /* already got one */ + {512, 100, VB_DISK_FLAG_REMOVABLE, "holygrail"}, + }, + .disk_count_to_return = DEFAULT_COUNT, + .diskgetinfo_return_val = VBERROR_SUCCESS, + .loadkernel_return_val = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1,}, + + .expected_recovery_request_val = VBNV_RECOVERY_NOT_REQUESTED, + .expected_to_find_disk = pickme, + .expected_to_load_disk = pickme, + .expected_return_val = VBERROR_SUCCESS + }, + { + .name = "second removable drive", + .want_flags = VB_DISK_FLAG_REMOVABLE, + .disks_to_provide = { + /* wrong flags */ + {512, 100, 0, 0}, + {512, 100, VB_DISK_FLAG_REMOVABLE, "not yet"}, + {512, 100, VB_DISK_FLAG_REMOVABLE, pickme}, + }, + .disk_count_to_return = DEFAULT_COUNT, + .diskgetinfo_return_val = VBERROR_SUCCESS, + .loadkernel_return_val = {1, 0, 1, 1, 1, 1, 1, 1, 1, 1,}, + + .expected_recovery_request_val = VBNV_RECOVERY_NOT_REQUESTED, + .expected_to_find_disk = pickme, + .expected_to_load_disk = pickme, + .expected_return_val = VBERROR_SUCCESS + }, + { + .name = "first fixed drive", + .want_flags = VB_DISK_FLAG_FIXED, + .disks_to_provide = { + /* too small */ + {512, 10, VB_DISK_FLAG_FIXED, 0}, + /* wrong LBA */ + {2048, 100, VB_DISK_FLAG_FIXED, 0}, + /* wrong type */ + {512, 100, VB_DISK_FLAG_REMOVABLE, 0}, + /* wrong flags */ + {512, 100, 0, 0}, + /* still wrong flags */ + {512, 100, -1, 0}, + /* flags */ + {512, 100, VB_DISK_FLAG_REMOVABLE|VB_DISK_FLAG_FIXED, + 0}, + {512, 100, VB_DISK_FLAG_FIXED, pickme}, + /* already got one */ + {512, 100, VB_DISK_FLAG_FIXED, "holygrail"}, + }, + .disk_count_to_return = DEFAULT_COUNT, + .diskgetinfo_return_val = VBERROR_SUCCESS, + .loadkernel_return_val = {0, 1, 1, 1, 1, 1, 1, 1, 1, 1,}, + + .expected_recovery_request_val = VBNV_RECOVERY_NOT_REQUESTED, + .expected_to_find_disk = pickme, + .expected_to_load_disk = pickme, + .expected_return_val = VBERROR_SUCCESS + }, + { + .name = "no drives at all", + .want_flags = VB_DISK_FLAG_FIXED, + .disks_to_provide = {}, + .disk_count_to_return = DEFAULT_COUNT, + .diskgetinfo_return_val = VBERROR_SUCCESS, + .loadkernel_return_val = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1,}, + + .expected_recovery_request_val = VBNV_RECOVERY_RW_NO_DISK, + .expected_to_find_disk = 0, + .expected_to_load_disk = 0, + .expected_return_val = VBERROR_NO_DISK_FOUND + }, + { + .name = "no valid drives", + .want_flags = VB_DISK_FLAG_FIXED, + .disks_to_provide = { + /* too small */ + {512, 10, VB_DISK_FLAG_FIXED, 0}, + /* wrong LBA */ + {2048, 100, VB_DISK_FLAG_FIXED, 0}, + /* wrong type */ + {512, 100, VB_DISK_FLAG_REMOVABLE, 0}, + /* wrong flags */ + {512, 100, 0, 0}, + /* still wrong flags */ + {512, 100, -1, 0}, + /* doesn't load */ + {512, 100, VB_DISK_FLAG_FIXED, "bad1"}, + /* doesn't load */ + {512, 100, VB_DISK_FLAG_FIXED, "bad2"}, + }, + .disk_count_to_return = DEFAULT_COUNT, + .diskgetinfo_return_val = VBERROR_SUCCESS, + .loadkernel_return_val = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1,}, + + .expected_recovery_request_val = VBNV_RECOVERY_RW_NO_KERNEL, + .expected_to_find_disk = DONT_CARE, + .expected_to_load_disk = 0, + .expected_return_val = 1 + }, }; - /****************************************************************************/ /* Mock data */ @@ -168,153 +182,134 @@ static const char *got_find_disk; static const char *got_load_disk; static uint32_t got_return_val; -/* Reset mock data (for use before each test) */ -static void ResetMocks(int i) { - Memset(&lkparams, 0, sizeof(lkparams)); - Memset(&mock_disks, 0, sizeof(mock_disks)); - load_kernel_calls = 0; - - got_recovery_request_val = VBNV_RECOVERY_NOT_REQUESTED; - got_find_disk = 0; - got_load_disk = 0; - got_return_val = 0xdeadbeef; - - t = test+i; +/** + * Reset mock data (for use before each test) + */ +static void ResetMocks(int i) +{ + Memset(&lkparams, 0, sizeof(lkparams)); + Memset(&mock_disks, 0, sizeof(mock_disks)); + load_kernel_calls = 0; + + got_recovery_request_val = VBNV_RECOVERY_NOT_REQUESTED; + got_find_disk = 0; + got_load_disk = 0; + got_return_val = 0xdeadbeef; + + t = test + i; } -int is_nonzero(const void *vptr, size_t count) { - const char *p = (const char *)vptr; - while (count--) - if (*p++) - return 1; - - return 0; -} +int is_nonzero(const void *vptr, size_t count) +{ + const char *p = (const char *)vptr; + while (count--) + if (*p++) + return 1; -int CheckResults(void) { - VBDEBUG(("%s()\n", __FUNCTION__)); - VBDEBUG((" recovery_request: %x %x\n", - t->expected_recovery_request_val, got_recovery_request_val)); - VBDEBUG((" find_disk: (%s) (%s)\n", - (t->expected_to_find_disk == DONT_CARE - ? "DONT CARE" - : t->expected_to_find_disk), - got_find_disk)); - VBDEBUG((" load_disk: (%s) (%s)\n", - (t->expected_to_load_disk == DONT_CARE - ? "DONT CARE" - : t->expected_to_load_disk), - got_load_disk)); - VBDEBUG((" return_val: %x %x\n", - t->expected_return_val, got_return_val)); - return (t->expected_recovery_request_val == got_recovery_request_val && - (t->expected_to_find_disk == DONT_CARE || - t->expected_to_find_disk == got_find_disk) && - (t->expected_to_load_disk == DONT_CARE || - t->expected_to_load_disk == got_load_disk) && - t->expected_return_val == got_return_val); + return 0; } /****************************************************************************/ /* Mocked verification functions */ -VbError_t VbExDiskGetInfo(VbDiskInfo** infos_ptr, uint32_t* count, - uint32_t disk_flags) { - int i; - int num_disks = 0; - - VBDEBUG(("My %s\n", __FUNCTION__)); - - *infos_ptr = mock_disks; - - for(i=0; i<MAX_TEST_DISKS; i++) { - if (is_nonzero(&t->disks_to_provide[i], sizeof(t->disks_to_provide[i]))) { - mock_disks[num_disks].bytes_per_lba = - t->disks_to_provide[i].bytes_per_lba; - mock_disks[num_disks].lba_count = t->disks_to_provide[i].lba_count; - mock_disks[num_disks].flags = t->disks_to_provide[i].flags; - mock_disks[num_disks].handle = - (VbExDiskHandle_t)t->disks_to_provide[i].diskname; - VBDEBUG((" mock_disk[%d] %lld %lld 0x%x %s\n", i, - mock_disks[num_disks].bytes_per_lba, - mock_disks[num_disks].lba_count, - mock_disks[num_disks].flags, - (mock_disks[num_disks].handle - ? (char *)mock_disks[num_disks].handle - : "0"))); - num_disks++; - } else { - mock_disks[num_disks].handle = (VbExDiskHandle_t)"INVALID"; - } - } - - if (t->disk_count_to_return >= 0) - *count = t->disk_count_to_return; - else - *count = num_disks; - - VBDEBUG((" *count=%lld\n", *count)); - VBDEBUG((" return 0x%x\n", t->diskgetinfo_return_val)); - - return t->diskgetinfo_return_val; +VbError_t VbExDiskGetInfo(VbDiskInfo **infos_ptr, uint32_t *count, + uint32_t disk_flags) +{ + int i; + int num_disks = 0; + + VBDEBUG(("My %s\n", __FUNCTION__)); + + *infos_ptr = mock_disks; + + for(i = 0; i < MAX_TEST_DISKS; i++) { + if (is_nonzero(&t->disks_to_provide[i], + sizeof(t->disks_to_provide[i]))) { + mock_disks[num_disks].bytes_per_lba = + t->disks_to_provide[i].bytes_per_lba; + mock_disks[num_disks].lba_count = + t->disks_to_provide[i].lba_count; + mock_disks[num_disks].flags = + t->disks_to_provide[i].flags; + mock_disks[num_disks].handle = (VbExDiskHandle_t) + t->disks_to_provide[i].diskname; + VBDEBUG((" mock_disk[%d] %lld %lld 0x%x %s\n", i, + mock_disks[num_disks].bytes_per_lba, + mock_disks[num_disks].lba_count, + mock_disks[num_disks].flags, + (mock_disks[num_disks].handle + ? (char *)mock_disks[num_disks].handle + : "0"))); + num_disks++; + } else { + mock_disks[num_disks].handle = + (VbExDiskHandle_t)"INVALID"; + } + } + + if (t->disk_count_to_return >= 0) + *count = t->disk_count_to_return; + else + *count = num_disks; + + VBDEBUG((" *count=%lld\n", *count)); + VBDEBUG((" return 0x%x\n", t->diskgetinfo_return_val)); + + return t->diskgetinfo_return_val; } -VbError_t VbExDiskFreeInfo(VbDiskInfo* infos, - VbExDiskHandle_t preserve_handle) { - got_load_disk = (const char *)preserve_handle; - VBDEBUG(("%s(): got_load_disk = %s\n", __FUNCTION__, - got_load_disk ? got_load_disk : "0")); - return VBERROR_SUCCESS; +VbError_t VbExDiskFreeInfo(VbDiskInfo *infos, + VbExDiskHandle_t preserve_handle) +{ + got_load_disk = (const char *)preserve_handle; + VBDEBUG(("%s(): got_load_disk = %s\n", __FUNCTION__, + got_load_disk ? got_load_disk : "0")); + return VBERROR_SUCCESS; } -VbError_t LoadKernel(LoadKernelParams* params) { - got_find_disk = (const char *)params->disk_handle; - VBDEBUG(("%s(%d): got_find_disk = %s\n", __FUNCTION__, - load_kernel_calls, - got_find_disk ? got_find_disk : "0")); - return t->loadkernel_return_val[load_kernel_calls++]; +VbError_t LoadKernel(LoadKernelParams *params) +{ + got_find_disk = (const char *)params->disk_handle; + VBDEBUG(("%s(%d): got_find_disk = %s\n", __FUNCTION__, + load_kernel_calls, + got_find_disk ? got_find_disk : "0")); + return t->loadkernel_return_val[load_kernel_calls++]; } -int VbNvSet(VbNvContext* context, VbNvParam param, uint32_t value) { - VBDEBUG(("%s(): got_recovery_request_val = %d (0x%x)\n", __FUNCTION__, - value, value)); - got_recovery_request_val = value; - return 0; +int VbNvSet(VbNvContext *context, VbNvParam param, uint32_t value) +{ + VBDEBUG(("%s(): got_recovery_request_val = %d (0x%x)\n", __FUNCTION__, + value, value)); + got_recovery_request_val = value; + return 0; } /****************************************************************************/ - - -/* This is not declared in any headers, so declare it here. */ -uint32_t VbTryLoadKernel(VbCommonParams* cparams, LoadKernelParams* p, - uint32_t get_info_flags); - - -static void VbTryLoadKernelTest(void) { - int i; - int num_tests = sizeof(test) / sizeof(test[0]); - - for (i=0; i<num_tests; i++) { - VBDEBUG(("STARTING %s ...\n", test[i].name)); - ResetMocks(i); - got_return_val = VbTryLoadKernel(0, &lkparams, test[i].want_flags); - VBDEBUG(("VbTryLoadKernel(): got_return_val = 0x%x\n", got_return_val)); - TEST_TRUE(CheckResults(), test[i].name); - } +static void VbTryLoadKernelTest(void) +{ + int i; + int num_tests = sizeof(test) / sizeof(test[0]); + + for (i = 0; i < num_tests; i++) { + printf("Test case: %s ...\n", test[i].name); + ResetMocks(i); + TEST_EQ(VbTryLoadKernel(0, &lkparams, test[i].want_flags), + t->expected_return_val, " return value"); + TEST_EQ(got_recovery_request_val, + t->expected_recovery_request_val, " recovery_request"); + if (t->expected_to_find_disk != DONT_CARE) { + TEST_PTR_EQ(got_find_disk, t->expected_to_find_disk, + " find disk"); + TEST_PTR_EQ(got_load_disk, t->expected_to_load_disk, + " load disk"); + } + } } +int main(void) +{ + VbTryLoadKernelTest(); -/* disable MSVC warnings on unused arguments */ -__pragma(warning (disable: 4100)) - -int main(int argc, char* argv[]) { - int error_code = 0; - - VbTryLoadKernelTest(); - - if (!gTestSuccess) - error_code = 255; - - return error_code; + return gTestSuccess ? 0 : 255; } diff --git a/tests/vboot_display_tests.c b/tests/vboot_display_tests.c index 5713667b..3093534d 100644 --- a/tests/vboot_display_tests.c +++ b/tests/vboot_display_tests.c @@ -9,6 +9,7 @@ #include <stdlib.h> #include <string.h> +#include "bmpblk_font.h" #include "gbb_header.h" #include "host_common.h" #include "test_common.h" @@ -21,21 +22,41 @@ 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 gbb_data[4096 + sizeof(GoogleBinaryBlockHeader)]; +static GoogleBinaryBlockHeader *gbb = (GoogleBinaryBlockHeader *)gbb_data; +static BmpBlockHeader *bhdr; static char debug_info[4096]; /* Reset mock data (for use before each test) */ static void ResetMocks(void) { + int gbb_used; + + Memset(gbb_data, 0, sizeof(gbb_data)); + gbb->major_version = GBB_MAJOR_VER; + gbb->minor_version = GBB_MINOR_VER; + gbb->flags = 0; + gbb_used = sizeof(GoogleBinaryBlockHeader); + + gbb->hwid_offset = gbb_used; + strcpy(gbb_data + gbb->hwid_offset, "Test HWID"); + gbb->hwid_size = strlen(gbb_data + gbb->hwid_offset) + 1; + gbb_used = (gbb_used + gbb->hwid_size + 7) & ~7; + + gbb->bmpfv_offset = gbb_used; + bhdr = (BmpBlockHeader *)(gbb_data + gbb->bmpfv_offset); + gbb->bmpfv_size = sizeof(BmpBlockHeader); + gbb_used = (gbb_used + gbb->bmpfv_size + 7) & ~7; + memcpy(bhdr->signature, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE); + bhdr->major_version = BMPBLOCK_MAJOR_VERSION; + bhdr->minor_version = BMPBLOCK_MINOR_VERSION; + bhdr->number_of_localizations = 3; + 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; + cparams.gbb_data = gbb; + cparams.gbb_size = sizeof(gbb_data); Memset(&vnc, 0, sizeof(vnc)); VbNvSetup(&vnc); @@ -65,19 +86,149 @@ static void DebugInfoTest(void) for (i = 0; i < 0x100; i++) TEST_PTR_NEQ(RecoveryReasonString(i), NULL, "Non-null reason"); + /* HWID should come from the gbb */ + ResetMocks(); + TEST_EQ(strcmp(VbHWID(&cparams), "Test HWID"), 0, "HWID"); + + ResetMocks(); + cparams.gbb_size = 0; + TEST_EQ(strcmp(VbHWID(&cparams), "{INVALID}"), 0, "HWID bad gbb"); + + ResetMocks(); + gbb->hwid_size = 0; + TEST_EQ(strcmp(VbHWID(&cparams), "{INVALID}"), 0, "HWID missing"); + + ResetMocks(); + gbb->hwid_offset = cparams.gbb_size + 1; + TEST_EQ(strcmp(VbHWID(&cparams), "{INVALID}"), 0, "HWID past end"); + + ResetMocks(); + gbb->hwid_size = cparams.gbb_size; + TEST_EQ(strcmp(VbHWID(&cparams), "{INVALID}"), 0, "HWID overflow"); + /* Display debug info */ + ResetMocks(); VbDisplayDebugInfo(&cparams, &vnc); TEST_NEQ(*debug_info, '\0', "Some debug info was displayed"); } -/* disable MSVC warnings on unused arguments */ -__pragma(warning (disable: 4100)) +/* Test localization */ +static void LocalizationTest(void) +{ + uint32_t count = 6; + + ResetMocks(); + gbb->bmpfv_size = 0; + TEST_EQ(VbGetLocalizationCount(&cparams, &count), + VBERROR_INVALID_GBB, "VbGetLocalizationCount bad gbb"); + TEST_EQ(count, 0, " count"); + + ResetMocks(); + bhdr->signature[0] ^= 0x5a; + TEST_EQ(VbGetLocalizationCount(&cparams, &count), + VBERROR_INVALID_BMPFV, "VbGetLocalizationCount bad bmpfv"); + + ResetMocks(); + TEST_EQ(VbGetLocalizationCount(&cparams, &count), 0, + "VbGetLocalizationCount()"); + TEST_EQ(count, 3, " count"); + +} + +/* Test display key checking */ +static void DisplayKeyTest(void) +{ + uint32_t u; + + ResetMocks(); + VbCheckDisplayKey(&cparams, 'q', &vnc); + TEST_EQ(*debug_info, '\0', "DisplayKey q = does nothing"); + + ResetMocks(); + VbCheckDisplayKey(&cparams, '\t', &vnc); + TEST_NEQ(*debug_info, '\0', "DisplayKey tab = display"); + + /* Toggle localization */ + ResetMocks(); + VbNvSet(&vnc, VBNV_LOCALIZATION_INDEX, 0); + VbNvTeardown(&vnc); + VbCheckDisplayKey(&cparams, VB_KEY_DOWN, &vnc); + VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u); + TEST_EQ(u, 2, "DisplayKey up"); + VbCheckDisplayKey(&cparams, VB_KEY_LEFT, &vnc); + VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u); + TEST_EQ(u, 1, "DisplayKey left"); + VbCheckDisplayKey(&cparams, VB_KEY_RIGHT, &vnc); + VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u); + TEST_EQ(u, 2, "DisplayKey right"); + VbCheckDisplayKey(&cparams, VB_KEY_UP, &vnc); + VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u); + TEST_EQ(u, 0, "DisplayKey up"); + + /* Reset localization if localization count is invalid */ + ResetMocks(); + VbNvSet(&vnc, VBNV_LOCALIZATION_INDEX, 1); + VbNvTeardown(&vnc); + bhdr->signature[0] ^= 0x5a; + VbCheckDisplayKey(&cparams, VB_KEY_UP, &vnc); + VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u); + TEST_EQ(u, 0, "DisplayKey invalid"); + +} -int main(int argc, char* argv[]) +static void FontTest(void) { - ResetMocks(); // KLUDGE + FontArrayHeader h; + FontArrayEntryHeader eh[3] = { + { + .ascii = 'A', + .info.original_size = 10, + }, + { + .ascii = 'B', + .info.original_size = 20, + }, + { + .ascii = 'C', + .info.original_size = 30, + }, + }; + FontArrayEntryHeader *eptr; + uint8_t buf[sizeof(h) + sizeof(eh)]; + VbFont_t *fptr; + void *bufferptr; + uint32_t buffersize; + + /* Create font data */ + h.num_entries = ARRAY_SIZE(eh); + Memcpy(buf, &h, sizeof(h)); + eptr = (FontArrayEntryHeader *)(buf + sizeof(h)); + Memcpy(eptr, eh, sizeof(eh)); + + fptr = VbInternalizeFontData((FontArrayHeader *)buf); + TEST_PTR_EQ(fptr, buf, "Internalize"); + + TEST_PTR_EQ(VbFindFontGlyph(fptr, 'B', &bufferptr, &buffersize), + &eptr[1].info, "Glyph found"); + TEST_EQ(buffersize, eptr[1].info.original_size, " size"); + TEST_PTR_EQ(VbFindFontGlyph(fptr, 'X', &bufferptr, &buffersize), + &eptr[0].info, "Glyph not found"); + TEST_EQ(buffersize, eptr[0].info.original_size, " size"); + + /* Test invalid rendering params */ + VbRenderTextAtPos(NULL, 0, 0, 0, fptr); + VbRenderTextAtPos("ABC", 0, 0, 0, NULL); + + VbDoneWithFontForNow(fptr); +} + +int main(void) +{ DebugInfoTest(); + LocalizationTest(); + DisplayKeyTest(); + FontTest(); return gTestSuccess ? 0 : 255; } diff --git a/tests/vboot_kernel_tests.c b/tests/vboot_kernel_tests.c new file mode 100644 index 00000000..1ee1528f --- /dev/null +++ b/tests/vboot_kernel_tests.c @@ -0,0 +1,538 @@ +/* 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 vboot_kernel.c + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "cgptlib.h" +#include "gbb_header.h" +#include "gpt.h" +#include "host_common.h" +#include "load_kernel_fw.h" +#include "test_common.h" +#include "vboot_api.h" +#include "vboot_common.h" +#include "vboot_kernel.h" +#include "vboot_nvstorage.h" + +#define LOGCALL(fmt, args...) sprintf(call_log + strlen(call_log), fmt, ##args) +#define TEST_CALLS(expect_log) TEST_STR_EQ(call_log, expect_log, " calls") + +/* Mock kernel partition */ +struct mock_part { + uint32_t start; + uint32_t size; +}; + +/* Partition list; ends with a 0-size partition. */ +#define MOCK_PART_COUNT 8 +static struct mock_part mock_parts[MOCK_PART_COUNT]; +static int mock_part_next; + +/* Mock data */ +static char call_log[4096]; +static uint8_t kernel_buffer[80000]; +static int disk_read_to_fail; +static int disk_write_to_fail; +static int gpt_init_fail; +static int key_block_verify_fail; /* 0=ok, 1=sig, 2=hash */ +static int preamble_verify_fail; +static int verify_data_fail; +static RSAPublicKey *mock_data_key; +static int mock_data_key_allocated; + +static GoogleBinaryBlockHeader gbb; +static VbExDiskHandle_t handle; +static VbNvContext vnc; +static uint8_t shared_data[VB_SHARED_DATA_MIN_SIZE]; +static VbSharedDataHeader *shared = (VbSharedDataHeader *)shared_data; +static LoadKernelParams lkp; +static VbKeyBlockHeader kbh; +static VbKernelPreambleHeader kph; + +static void ResetCallLog(void) +{ + *call_log = 0; +} + +/** + * Reset mock data (for use before each test) + */ +static void ResetMocks(void) +{ + ResetCallLog(); + + disk_read_to_fail = -1; + disk_write_to_fail = -1; + + gpt_init_fail = 0; + key_block_verify_fail = 0; + preamble_verify_fail = 0; + verify_data_fail = 0; + + mock_data_key = (RSAPublicKey *)"TestDataKey"; + mock_data_key_allocated = 0; + + 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)); + shared->kernel_version_tpm = 0x20001; + + memset(&lkp, 0, sizeof(lkp)); + lkp.nv_context = &vnc; + lkp.shared_data_blob = shared; + lkp.gbb_data = &gbb; + lkp.bytes_per_lba = 512; + lkp.ending_lba = 1023; + lkp.kernel_buffer = kernel_buffer; + lkp.kernel_buffer_size = sizeof(kernel_buffer); + + memset(&kbh, 0, sizeof(kbh)); + kbh.data_key.key_version = 2; + kbh.key_block_flags = -1; + kbh.key_block_size = sizeof(kbh); + + memset(&kph, 0, sizeof(kph)); + kph.kernel_version = 1; + kph.preamble_size = 4096 - kbh.key_block_size; + kph.body_signature.data_size = 70000; + kph.bootloader_address = 0xbeadd008; + kph.bootloader_size = 0x1234; + + memset(mock_parts, 0, sizeof(mock_parts)); + mock_parts[0].start = 100; + mock_parts[0].size = 150; /* 75 KB */ + mock_part_next = 0; +} + +/* Mocks */ + +VbError_t VbExDiskRead(VbExDiskHandle_t handle, uint64_t lba_start, + uint64_t lba_count, void *buffer) +{ + LOGCALL("VbExDiskRead(h, %d, %d)\n", (int)lba_start, (int)lba_count); + + if ((int)lba_start == disk_read_to_fail) + return VBERROR_SIMULATED; + + return VBERROR_SUCCESS; +} + +VbError_t VbExDiskWrite(VbExDiskHandle_t handle, uint64_t lba_start, + uint64_t lba_count, const void *buffer) +{ + LOGCALL("VbExDiskWrite(h, %d, %d)\n", (int)lba_start, (int)lba_count); + + if ((int)lba_start == disk_write_to_fail) + return VBERROR_SIMULATED; + + return VBERROR_SUCCESS; +} + +int GptInit(GptData *gpt) +{ + return gpt_init_fail; +} + +int GptNextKernelEntry(GptData *gpt, uint64_t *start_sector, uint64_t *size) +{ + struct mock_part *p = mock_parts + mock_part_next; + + if (!p->size) + return GPT_ERROR_NO_VALID_KERNEL; + + gpt->current_kernel = mock_part_next; + *start_sector = p->start; + *size = p->size; + mock_part_next++; + return GPT_SUCCESS; +} + +void GetCurrentKernelUniqueGuid(GptData *gpt, void *dest) +{ + static char fake_guid[] = "FakeGuid"; + + memcpy(dest, fake_guid, sizeof(fake_guid)); +} + +int KeyBlockVerify(const VbKeyBlockHeader *block, uint64_t size, + const VbPublicKey *key, int hash_only) { + + if (hash_only && key_block_verify_fail >= 2) + return VBERROR_SIMULATED; + else if (!hash_only && key_block_verify_fail >= 1) + return VBERROR_SIMULATED; + + /* Use this as an opportunity to override the key block */ + memcpy((void *)block, &kbh, sizeof(kbh)); + return VBERROR_SUCCESS; +} + +RSAPublicKey *PublicKeyToRSA(const VbPublicKey *key) +{ + TEST_EQ(mock_data_key_allocated, 0, " mock data key not allocated"); + + if (mock_data_key) + mock_data_key_allocated++; + + return mock_data_key; +} + +void RSAPublicKeyFree(RSAPublicKey* key) +{ + TEST_EQ(mock_data_key_allocated, 1, " mock data key allocated"); + TEST_PTR_EQ(key, mock_data_key, " data key ptr"); + mock_data_key_allocated--; +} + +int VerifyKernelPreamble(const VbKernelPreambleHeader *preamble, + uint64_t size, const RSAPublicKey *key) +{ + if (preamble_verify_fail) + return VBERROR_SIMULATED; + + /* Use this as an opportunity to override the preamble */ + memcpy((void *)preamble, &kph, sizeof(kph)); + return VBERROR_SUCCESS; +} + +int VerifyData(const uint8_t *data, uint64_t size, const VbSignature *sig, + const RSAPublicKey *key) +{ + if (verify_data_fail) + return VBERROR_SIMULATED; + + return VBERROR_SUCCESS; +} + + +/** + * Test reading/writing GPT + */ +static void ReadWriteGptTest(void) +{ + GptData g; + GptHeader *h; + + g.sector_bytes = 512; + g.drive_sectors = 1024; + + ResetMocks(); + TEST_EQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead"); + TEST_CALLS("VbExDiskRead(h, 1, 1)\n" + "VbExDiskRead(h, 2, 32)\n" + "VbExDiskRead(h, 991, 32)\n" + "VbExDiskRead(h, 1023, 1)\n"); + ResetCallLog(); + TEST_EQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree"); + TEST_CALLS(""); + + /* Data which is changed is written */ + ResetMocks(); + AllocAndReadGptData(handle, &g); + g.modified |= GPT_MODIFIED_HEADER1 | GPT_MODIFIED_ENTRIES1; + ResetCallLog(); + TEST_EQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree mod 1"); + TEST_CALLS("VbExDiskWrite(h, 1, 1)\n" + "VbExDiskWrite(h, 2, 32)\n"); + + /* Data which is changed is written */ + ResetMocks(); + AllocAndReadGptData(handle, &g); + g.modified = -1; + ResetCallLog(); + TEST_EQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree mod all"); + TEST_CALLS("VbExDiskWrite(h, 1, 1)\n" + "VbExDiskWrite(h, 2, 32)\n" + "VbExDiskWrite(h, 991, 32)\n" + "VbExDiskWrite(h, 1023, 1)\n"); + + /* If legacy signature, don't modify GPT header/entries 1 */ + ResetMocks(); + AllocAndReadGptData(handle, &g); + h = (GptHeader *)g.primary_header; + memcpy(h->signature, GPT_HEADER_SIGNATURE2, GPT_HEADER_SIGNATURE_SIZE); + g.modified = -1; + ResetCallLog(); + TEST_EQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree mod all"); + TEST_CALLS("VbExDiskWrite(h, 991, 32)\n" + "VbExDiskWrite(h, 1023, 1)\n"); + + /* Error reading */ + ResetMocks(); + disk_read_to_fail = 1; + TEST_NEQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail"); + WriteAndFreeGptData(handle, &g); + + ResetMocks(); + disk_read_to_fail = 2; + TEST_NEQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail"); + WriteAndFreeGptData(handle, &g); + + ResetMocks(); + disk_read_to_fail = 991; + TEST_NEQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail"); + WriteAndFreeGptData(handle, &g); + + ResetMocks(); + disk_read_to_fail = 1023; + TEST_NEQ(AllocAndReadGptData(handle, &g), 0, "AllocAndRead disk fail"); + WriteAndFreeGptData(handle, &g); + + /* Error writing */ + ResetMocks(); + disk_write_to_fail = 1; + AllocAndReadGptData(handle, &g); + g.modified = -1; + TEST_NEQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree disk fail"); + + ResetMocks(); + disk_write_to_fail = 2; + AllocAndReadGptData(handle, &g); + g.modified = -1; + TEST_NEQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree disk fail"); + + ResetMocks(); + disk_write_to_fail = 991; + AllocAndReadGptData(handle, &g); + g.modified = -1; + TEST_NEQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree disk fail"); + + ResetMocks(); + disk_write_to_fail = 1023; + AllocAndReadGptData(handle, &g); + g.modified = -1; + TEST_NEQ(WriteAndFreeGptData(handle, &g), 0, "WriteAndFree disk fail"); + +} + +/** + * Trivial invalid calls to LoadKernel() + */ +static void InvalidParamsTest(void) +{ + ResetMocks(); + lkp.bytes_per_lba = 0; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_PARAMETER, "Bad lba size"); + + ResetMocks(); + lkp.ending_lba = 0; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_PARAMETER, "Bad lba count"); + + ResetMocks(); + lkp.bytes_per_lba = 128*1024; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_PARAMETER, "Huge lba size"); + + ResetMocks(); + disk_read_to_fail = 1; + TEST_EQ(LoadKernel(&lkp), VBERROR_NO_KERNEL_FOUND, "Can't read disk"); + + ResetMocks(); + gpt_init_fail = 1; + TEST_EQ(LoadKernel(&lkp), VBERROR_NO_KERNEL_FOUND, "Bad GPT"); +} + +static void LoadKernelTest(void) +{ + uint32_t u; + + ResetMocks(); + TEST_EQ(LoadKernel(&lkp), 0, "First kernel good"); + TEST_EQ(lkp.partition_number, 1, " part num"); + TEST_EQ(lkp.bootloader_address, 0xbeadd008, " bootloader addr"); + TEST_EQ(lkp.bootloader_size, 0x1234, " bootloader size"); + TEST_STR_EQ((char *)lkp.partition_guid, "FakeGuid", " guid"); + VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &u); + TEST_EQ(u, 0, " recovery request"); + + ResetMocks(); + mock_parts[1].start = 300; + mock_parts[1].size = 150; + TEST_EQ(LoadKernel(&lkp), 0, "Two good kernels"); + TEST_EQ(lkp.partition_number, 1, " part num"); + TEST_EQ(mock_part_next, 1, " didn't read second one"); + + /* Fail if no kernels found */ + ResetMocks(); + mock_parts[0].size = 0; + TEST_EQ(LoadKernel(&lkp), VBERROR_NO_KERNEL_FOUND, "No kernels"); + VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &u); + TEST_EQ(u, VBNV_RECOVERY_RW_NO_OS, " recovery request"); + + /* Skip kernels which are too small */ + ResetMocks(); + mock_parts[0].size = 10; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, "Too small"); + VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &u); + TEST_EQ(u, VBNV_RECOVERY_RW_INVALID_OS, " recovery request"); + + ResetMocks(); + disk_read_to_fail = 100; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Fail reading kernel start"); + + ResetMocks(); + key_block_verify_fail = 1; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Fail key block sig"); + + /* In dev mode, fail if hash is bad too */ + ResetMocks(); + lkp.boot_flags |= BOOT_FLAG_DEVELOPER; + key_block_verify_fail = 2; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Fail key block dev hash"); + + /* But just bad sig is ok */ + ResetMocks(); + lkp.boot_flags |= BOOT_FLAG_DEVELOPER; + key_block_verify_fail = 1; + TEST_EQ(LoadKernel(&lkp), 0, "Succeed key block dev sig"); + + /* In dev mode and requiring signed kernel, fail if sig is bad */ + ResetMocks(); + lkp.boot_flags |= BOOT_FLAG_DEVELOPER; + VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 1); + VbNvTeardown(&vnc); + key_block_verify_fail = 1; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Fail key block dev sig"); + + /* Check key block flag mismatches */ + ResetMocks(); + kbh.key_block_flags = + KEY_BLOCK_FLAG_RECOVERY_0 | KEY_BLOCK_FLAG_DEVELOPER_1; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Key block dev flag mismatch"); + + ResetMocks(); + kbh.key_block_flags = + KEY_BLOCK_FLAG_RECOVERY_1 | KEY_BLOCK_FLAG_DEVELOPER_0; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Key block rec flag mismatch"); + + ResetMocks(); + lkp.boot_flags |= BOOT_FLAG_RECOVERY; + kbh.key_block_flags = + KEY_BLOCK_FLAG_RECOVERY_1 | KEY_BLOCK_FLAG_DEVELOPER_1; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Key block recdev flag mismatch"); + + ResetMocks(); + lkp.boot_flags |= BOOT_FLAG_RECOVERY | BOOT_FLAG_DEVELOPER; + kbh.key_block_flags = + KEY_BLOCK_FLAG_RECOVERY_1 | KEY_BLOCK_FLAG_DEVELOPER_0; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Key block rec!dev flag mismatch"); + + ResetMocks(); + kbh.data_key.key_version = 1; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Key block kernel key rollback"); + + ResetMocks(); + kbh.data_key.key_version = 0x10000; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Key block kernel key version too big"); + + ResetMocks(); + kbh.data_key.key_version = 3; + TEST_EQ(LoadKernel(&lkp), 0, "Key block version roll forward"); + TEST_EQ(shared->kernel_version_tpm, 0x30001, " shared version"); + + ResetMocks(); + kbh.data_key.key_version = 3; + mock_parts[1].start = 300; + mock_parts[1].size = 150; + TEST_EQ(LoadKernel(&lkp), 0, "Two kernels roll forward"); + TEST_EQ(mock_part_next, 2, " read both"); + TEST_EQ(shared->kernel_version_tpm, 0x30001, " shared version"); + + ResetMocks(); + kbh.data_key.key_version = 1; + lkp.boot_flags |= BOOT_FLAG_DEVELOPER; + TEST_EQ(LoadKernel(&lkp), 0, "Key version ignored in dev mode"); + + ResetMocks(); + kbh.data_key.key_version = 1; + lkp.boot_flags |= BOOT_FLAG_RECOVERY; + TEST_EQ(LoadKernel(&lkp), 0, "Key version ignored in rec mode"); + + ResetMocks(); + mock_data_key = NULL; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, "Bad data key"); + + ResetMocks(); + preamble_verify_fail = 1; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, "Bad preamble"); + + ResetMocks(); + kph.kernel_version = 0; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Kernel version rollback"); + + ResetMocks(); + kph.kernel_version = 0; + lkp.boot_flags |= BOOT_FLAG_DEVELOPER; + TEST_EQ(LoadKernel(&lkp), 0, "Kernel version ignored in dev mode"); + + ResetMocks(); + kph.kernel_version = 0; + lkp.boot_flags |= BOOT_FLAG_RECOVERY; + TEST_EQ(LoadKernel(&lkp), 0, "Kernel version ignored in rec mode"); + + ResetMocks(); + kph.preamble_size |= 0x07; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Kernel body offset"); + + /* Check getting kernel load address from header */ + ResetMocks(); + kph.body_load_address = (size_t)kernel_buffer; + lkp.kernel_buffer = NULL; + TEST_EQ(LoadKernel(&lkp), 0, "Get load address from preamble"); + TEST_PTR_EQ(lkp.kernel_buffer, kernel_buffer, " address"); + /* Size is rounded up to nearest sector */ + TEST_EQ(lkp.kernel_buffer_size, 70144, " size"); + + ResetMocks(); + lkp.kernel_buffer_size = 8192; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Kernel too big for buffer"); + + ResetMocks(); + mock_parts[0].size = 130; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Kernel too big for partition"); + + ResetMocks(); + disk_read_to_fail = 108; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, + "Fail reading kernel data"); + + ResetMocks(); + verify_data_fail = 1; + TEST_EQ(LoadKernel(&lkp), VBERROR_INVALID_KERNEL_FOUND, "Bad data"); +} + +int main(void) +{ + ReadWriteGptTest(); + InvalidParamsTest(); + LoadKernelTest(); + + return gTestSuccess ? 0 : 255; +} |