summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2011-03-21 16:58:49 -0700
committerRandall Spangler <rspangler@chromium.org>2011-03-21 16:58:49 -0700
commit85d55e287347401a779df438d57e7b10c9d57048 (patch)
tree67c44b13b6a0d813e860a6a330ec496468a0f961
parentf915de681c58b1fc6ccd03c48cdfe1a4a4ae3e1a (diff)
downloadvboot-85d55e287347401a779df438d57e7b10c9d57048.tar.gz
Cherry-pick vboot_reference files from TOT to support crossystem
These files are picked from vboot_reference TOT at this tag: 7141571d55373fc2a84a70b5663409a653f8049d There's about a dozen individual CLs in TOT which this is coalesced from. Rather than attempt to squash all of them, I've just pulled the current versions. This admittedly pulls in more files than I'd like in a single CL. Most of those are inside the firmware subdir, and are required for unit tests (esp. load_kernel_test) to compile but aren't used in the actual OS image. I have NOT pulled in any unreleated script changes, such as Gaurav's changes to the signing scripts. Change-Id: I3521a6eed40ff12c82545f2170090e755d7007fc R=dlaurie@chromium.org,reinauer@chromium.org,puneetster@chromium.org BUG=12904 TEST=manual 1. make && make runtests && make clean 2. emerge-x86-alex vboot_reference, then run the resulting crossystem utility on Alex 0053G1+ firmware Review URL: http://codereview.chromium.org/6719005
-rw-r--r--firmware/Makefile19
-rw-r--r--firmware/include/load_firmware_fw.h22
-rw-r--r--firmware/include/tlcl.h7
-rw-r--r--firmware/include/utility.h14
-rw-r--r--firmware/include/vboot_nvstorage.h27
-rw-r--r--firmware/include/vboot_struct.h151
-rw-r--r--firmware/lib/include/rollback_index.h8
-rw-r--r--firmware/lib/include/tpm_bootmode.h25
-rw-r--r--firmware/lib/rollback_index.c137
-rw-r--r--firmware/lib/tpm_bootmode.c134
-rw-r--r--firmware/lib/tpm_lite/include/tlcl_structures.h5
-rw-r--r--firmware/lib/tpm_lite/tlcl.c57
-rw-r--r--firmware/lib/vboot_common.c8
-rw-r--r--firmware/lib/vboot_firmware.c77
-rw-r--r--firmware/lib/vboot_kernel.c205
-rw-r--r--firmware/lib/vboot_nvstorage.c32
-rw-r--r--firmware/linktest/main.c5
-rw-r--r--firmware/stub/load_firmware_stub.c4
-rw-r--r--firmware/stub/utility_stub.c13
-rw-r--r--host/lib/crossystem.c338
-rw-r--r--tests/tpm_lite/timing.c18
-rw-r--r--tests/vboot_nvstorage_test.c4
-rw-r--r--utility/crossystem_main.c100
-rw-r--r--utility/load_kernel_test.c4
-rw-r--r--utility/tlcl_generator.c8
-rw-r--r--utility/tpmc.c1
26 files changed, 1211 insertions, 212 deletions
diff --git a/firmware/Makefile b/firmware/Makefile
index fcff8bb7..accc835c 100644
--- a/firmware/Makefile
+++ b/firmware/Makefile
@@ -15,6 +15,24 @@ ifeq ($(FIRMWARE_ARCH),)
CFLAGS += -DDISABLE_ROLLBACK_TPM
endif
+# TPM-specific flags. These depend on the particular TPM we're targeting for.
+# They are needed here only for compiling parts of the firmware code into
+# user-level tests.
+
+# TPM_BLOCKING_CONTINUESELFTEST is defined if TPM_ContinueSelfTest blocks until
+# the self test has completed.
+
+CLAGS += -DTPM_BLOCKING_CONTINUESELFTEST
+
+# TPM_MANUAL_SELFTEST is defined if the self test must be started manually
+# (with a call to TPM_ContinueSelfTest) instead of starting automatically at
+# power on.
+#
+# We sincerely hope that TPM_BLOCKING_CONTINUESELFTEST and TPM_MANUAL_SELFTEST
+# are not both defined at the same time. (See comment in code.)
+
+# CLAGS += -DTPM_MANUAL_SELFTEST
+
INCLUDES = \
-I$(FWTOP)/include \
-I$(LIBDIR)/include \
@@ -40,6 +58,7 @@ LIB_SRCS = \
./lib/cryptolib/sha2.c \
./lib/cryptolib/sha_utility.c \
./lib/rollback_index.c \
+ ./lib/tpm_bootmode.c \
./lib/stateful_util.c \
./lib/tpm_lite/tlcl.c \
./lib/utility.c \
diff --git a/firmware/include/load_firmware_fw.h b/firmware/include/load_firmware_fw.h
index 1e42f1c9..11fb0ce7 100644
--- a/firmware/include/load_firmware_fw.h
+++ b/firmware/include/load_firmware_fw.h
@@ -11,9 +11,7 @@
#include "sysincludes.h"
#include "vboot_nvstorage.h"
-
-/* Recommended size of shared_data_blob in bytes. */
-#define LOAD_FIRMWARE_SHARED_DATA_REC_SIZE 16384
+#include "vboot_struct.h"
/* Return codes for LoadFirmware() and S3Resume(). */
#define LOAD_FIRMWARE_SUCCESS 0 /* Success */
@@ -33,15 +31,19 @@ typedef struct LoadFirmwareParams {
void* verification_block_1; /* Key block + preamble for firmware 1 */
uint64_t verification_size_0; /* Verification block 0 size in bytes */
uint64_t verification_size_1; /* Verification block 1 size in bytes */
- void* shared_data_blob; /* Destination buffer for data shared between
- * LoadFirmware() and LoadKernel(). Pass this
+
+ /* Shared data blob for data shared between LoadFirmware() and LoadKernel().
+ * This should be at least VB_SHARED_DATA_MIN_SIZE bytes long, and ideally
+ * is VB_SHARED_DATA_REC_SIZE bytes long. */
+ void* shared_data_blob; /* Shared data blob buffer. Pass this
* data to LoadKernel() in
* LoadKernelParams.shared_data_blob. */
- uint64_t shared_data_size; /* Size of shared data blob buffer, in bytes.
- * On output, this will contain the actual
- * data size placed into the buffer. Caller
- * need only pass this much data to
- * LoadKernel().*/
+ uint64_t shared_data_size; /* On input, set to size of shared data blob
+ * buffer, in bytes. On output, this will
+ * contain the actual data size placed into
+ * the buffer. Caller need only pass that
+ * much data to LoadKernel().*/
+
uint64_t boot_flags; /* Boot flags */
VbNvContext* nv_context; /* Context for NV storage. nv_context->raw
* must be filled before calling
diff --git a/firmware/include/tlcl.h b/firmware/include/tlcl.h
index 95310541..10d08024 100644
--- a/firmware/include/tlcl.h
+++ b/firmware/include/tlcl.h
@@ -35,6 +35,11 @@ void TlclSetLogLevel(int level);
*/
uint32_t TlclStartup(void);
+/* Save the TPM state. Normally done by the kernel before a suspend, included
+ * here for tests. The TPM error code is returned (0 for success).
+ */
+uint32_t TlclSaveState(void);
+
/* Resumes by sending a TPM_Startup(ST_STATE). The TPM error code is returned
* (0 for success).
*/
@@ -127,7 +132,7 @@ uint32_t TlclSetGlobalLock(void);
/* Performs a TPM_Extend.
*/
-uint32_t TlclExtend(int pcr_num, uint8_t* in_digest, uint8_t* out_digest);
+uint32_t TlclExtend(int pcr_num, const uint8_t* in_digest, uint8_t* out_digest);
/* Gets the permission bits for the NVRAM space with |index|.
*/
diff --git a/firmware/include/utility.h b/firmware/include/utility.h
index 3c4a3bb5..4f88ab9c 100644
--- a/firmware/include/utility.h
+++ b/firmware/include/utility.h
@@ -96,4 +96,18 @@ int SafeMemcmp(const void* s1, const void* s2, size_t n);
#define memset _do_not_use_standard_memset
#endif
+/* Read a high-resolution timer. */
+uint64_t VbGetTimer(void);
+
+/* Return the maximum frequency for the high-resolution timer, in Hz.
+ *
+ * Note that this call MUST be fast; the implementation must not
+ * attempt to actually measure the frequency. This function need only
+ * return an upper bound for the timer frequency, so that minimum
+ * delays can be established. For example, if the same BIOS can run
+ * on CPUs where the timer frequency varies between 1.2GHz and 1.8GHz,
+ * return 1800000000 (or even 2000000000). */
+uint64_t VbGetTimerMaxFreq(void);
+
+
#endif /* VBOOT_REFERENCE_UTILITY_H_ */
diff --git a/firmware/include/vboot_nvstorage.h b/firmware/include/vboot_nvstorage.h
index 824dfac5..c2a722f9 100644
--- a/firmware/include/vboot_nvstorage.h
+++ b/firmware/include/vboot_nvstorage.h
@@ -47,13 +47,11 @@ typedef enum VbNvParam {
VBNV_LOCALIZATION_INDEX,
/* Field reserved for kernel/user-mode use; 32-bit value. */
VBNV_KERNEL_FIELD,
- /* Firmware checked RW slot B before slot A on the current boot because
- * VBNV_TRY_B_COUNT was non-zero at that time. 0=no; 1=yes. */
- VBNV_TRIED_FIRMWARE_B,
- /* Firmware verified the kernel key block signature using the key stored
- * in the firmware. 0=no, just used the key block hash; 1=yes, used the
- * key block signature. */
- VBNV_FW_VERIFIED_KERNEL_KEY,
+ /* Verified boot API function which should generate a test error, if
+ * error number (below) is non-zero. */
+ VBNV_TEST_ERROR_FUNC,
+ /* Verified boot API error to generate for the function, if non-zero. */
+ VBNV_TEST_ERROR_NUM,
} VbNvParam;
@@ -76,6 +74,12 @@ typedef enum VbNvParam {
#define VBNV_RECOVERY_RO_TPM_ERROR 0x05
/* Shared data error in read-only firmware */
#define VBNV_RECOVERY_RO_SHARED_DATA 0x06
+/* Test error from S3Resume() */
+#define VBNV_RECOVERY_RO_TEST_S3 0x07
+/* Test error from LoadFirmwareSetup() */
+#define VBNV_RECOVERY_RO_TEST_LFS 0x08
+/* Test error from LoadFirmware() */
+#define VBNV_RECOVERY_RO_TEST_LF 0x09
/* Unspecified/unknown error in read-only firmware */
#define VBNV_RECOVERY_RO_UNSPECIFIED 0x3F
/* User manually requested recovery by pressing a key at developer
@@ -91,6 +95,8 @@ typedef enum VbNvParam {
#define VBNV_RECOVERY_RW_DEV_MISMATCH 0x45
/* Shared data error in rewritable firmware */
#define VBNV_RECOVERY_RW_SHARED_DATA 0x46
+/* Test error from LoadKernel() */
+#define VBNV_RECOVERY_RW_TEST_LK 0x47
/* Unspecified/unknown error in rewritable firmware */
#define VBNV_RECOVERY_RW_UNSPECIFIED 0x7F
/* DM-verity error */
@@ -103,6 +109,13 @@ typedef enum VbNvParam {
#define VBNV_RECOVERY_US_UNSPECIFIED 0xFF
+/* Function codes for VBNV_TEST_ERROR_FUNC */
+#define VBNV_TEST_ERROR_LOAD_FIRMWARE_SETUP 1
+#define VBNV_TEST_ERROR_LOAD_FIRMWARE 2
+#define VBNV_TEST_ERROR_LOAD_KERNEL 3
+#define VBNV_TEST_ERROR_S3_RESUME 4
+
+
/* Initialize the NV storage library. This must be called before any
* other functions in this library. Returns 0 if success, non-zero if
* error.
diff --git a/firmware/include/vboot_struct.h b/firmware/include/vboot_struct.h
index 1e988ee1..f8fd8c80 100644
--- a/firmware/include/vboot_struct.h
+++ b/firmware/include/vboot_struct.h
@@ -131,10 +131,117 @@ typedef struct VbKernelPreambleHeader {
#define EXPECTED_VBKERNELPREAMBLEHEADER_SIZE 96
+/* Constants and sub-structures for VbSharedDataHeader */
+
+/* Magic number for recognizing VbSharedDataHeader ("VbSD") */
+#define VB_SHARED_DATA_MAGIC 0x44536256
+
/* Minimum and recommended size of shared_data_blob in bytes. */
#define VB_SHARED_DATA_MIN_SIZE 3072
#define VB_SHARED_DATA_REC_SIZE 16384
+/* Flags for VbSharedDataHeader */
+/* LoadFirmware() tried firmware B because of VbNvStorage firmware B tries */
+#define VBSD_FWB_TRIED 0x00000001
+/* LoadKernel() verified the good kernel keyblock using the kernel subkey from
+ * the firmware. If this flag is not present, it just used the hash of the
+ * kernel keyblock. */
+#define VBSD_KERNEL_KEY_VERIFIED 0x00000002
+/* LoadFirmware() was told the developer switch was on */
+#define VBSD_LF_DEV_SWITCH_ON 0x00000004
+
+/* Result codes for VbSharedDataHeader.check_fw_a_result (and b_result) */
+#define VBSD_LF_CHECK_NOT_DONE 0
+#define VBSD_LF_CHECK_DEV_MISMATCH 1
+#define VBSD_LF_CHECK_REC_MISMATCH 2
+#define VBSD_LF_CHECK_VERIFY_KEYBLOCK 3
+#define VBSD_LF_CHECK_KEY_ROLLBACK 4
+#define VBSD_LF_CHECK_DATA_KEY_PARSE 5
+#define VBSD_LF_CHECK_VERIFY_PREAMBLE 6
+#define VBSD_LF_CHECK_FW_ROLLBACK 7
+#define VBSD_LF_CHECK_HEADER_VALID 8
+#define VBSD_LF_CHECK_GET_FW_BODY 9
+#define VBSD_LF_CHECK_HASH_WRONG_SIZE 10
+#define VBSD_LF_CHECK_VERIFY_BODY 11
+#define VBSD_LF_CHECK_VALID 12
+
+/* Boot mode for VbSharedDataHeader.lk_boot_mode */
+#define VBSD_LK_BOOT_MODE_RECOVERY 0
+#define VBSD_LK_BOOT_MODE_NORMAL 1
+#define VBSD_LK_BOOT_MODE_DEVELOPER 2
+
+/* Flags for VbSharedDataKernelPart.flags */
+#define VBSD_LKP_FLAG_KEY_BLOCK_VALID 0x01
+
+/* Result codes for VbSharedDataKernelPart.check_result */
+#define VBSD_LKP_CHECK_NOT_DONE 0
+#define VBSD_LKP_CHECK_TOO_SMALL 1
+#define VBSD_LKP_CHECK_READ_START 2
+#define VBSD_LKP_CHECK_KEY_BLOCK_SIG 3
+#define VBSD_LKP_CHECK_KEY_BLOCK_HASH 4
+#define VBSD_LKP_CHECK_DEV_MISMATCH 5
+#define VBSD_LKP_CHECK_REC_MISMATCH 6
+#define VBSD_LKP_CHECK_KEY_ROLLBACK 7
+#define VBSD_LKP_CHECK_DATA_KEY_PARSE 8
+#define VBSD_LKP_CHECK_VERIFY_PREAMBLE 9
+#define VBSD_LKP_CHECK_KERNEL_ROLLBACK 10
+#define VBSD_LKP_CHECK_PREAMBLE_VALID 11
+#define VBSD_LKP_CHECK_BODY_ADDRESS 12
+#define VBSD_LKP_CHECK_BODY_OFFSET 13
+#define VBSD_LKP_CHECK_BODY_EXCEEDS_MEM 15
+#define VBSD_LKP_CHECK_BODY_EXCEEDS_PART 16
+#define VBSD_LKP_CHECK_READ_DATA 17
+#define VBSD_LKP_CHECK_VERIFY_DATA 18
+#define VBSD_LKP_CHECK_KERNEL_GOOD 19
+
+
+/* Information about a single kernel partition check in LoadKernel() */
+typedef struct VbSharedDataKernelPart {
+ uint64_t sector_start; /* Start sector of partition */
+ uint64_t sector_count; /* Sector count of partition */
+ uint32_t combined_version; /* Combined key+kernel version */
+ uint8_t gpt_index; /* Index of partition in GPT */
+ uint8_t check_result; /* Check result; see VBSD_LKP_CHECK_* */
+ uint8_t flags; /* Flags (see VBSD_LKP_FLAG_* */
+ uint8_t reserved0; /* Reserved for padding */
+} VbSharedDataKernelPart;
+
+/* Number of kernel partitions to track per call. Must be power of 2. */
+#define VBSD_MAX_KERNEL_PARTS 8
+
+/* Flags for VbSharedDataKernelCall.flags */
+/* Error initializing TPM in recovery mode */
+#define VBSD_LK_FLAG_REC_TPM_INIT_ERROR 0x00000001
+
+/* Result codes for VbSharedDataKernelCall.check_result */
+#define VBSD_LKC_CHECK_NOT_DONE 0
+#define VBSD_LKC_CHECK_DEV_SWITCH_MISMATCH 1
+#define VBSD_LKC_CHECK_GPT_READ_ERROR 2
+#define VBSD_LKC_CHECK_GPT_PARSE_ERROR 3
+#define VBSD_LKC_CHECK_GOOD_PARTITION 4
+#define VBSD_LKC_CHECK_INVALID_PARTITIONS 5
+#define VBSD_LKC_CHECK_NO_PARTITIONS 6
+
+/* Information about a single call to LoadKernel() */
+typedef struct VbSharedDataKernelCall {
+ uint32_t boot_flags; /* Bottom 32 bits of flags passed in
+ * LoadKernelParams.boot_flags */
+ uint32_t flags; /* Debug flags; see VBSD_LK_FLAG_* */
+ uint64_t sector_count; /* Number of sectors on drive */
+ uint32_t sector_size; /* Sector size in bytes */
+ uint8_t check_result; /* Check result; see VBSD_LKC_CHECK_* */
+ uint8_t boot_mode; /* Boot mode for LoadKernel(); see
+ * VBSD_LK_BOOT_MODE_* constants */
+ uint8_t test_error_num; /* Test error number, if non-zero */
+ uint8_t return_code; /* Return code from LoadKernel() */
+ uint8_t kernel_parts_found; /* Number of kernel partitions found */
+ uint8_t reserved0[7]; /* Reserved for padding */
+ VbSharedDataKernelPart parts[VBSD_MAX_KERNEL_PARTS]; /* Data on kernels */
+} VbSharedDataKernelCall;
+
+/* Number of kernel calls to track. Must be power of 2. */
+#define VBSD_MAX_KERNEL_CALLS 4
+
/* Data shared between LoadFirmware(), LoadKernel(), and OS.
*
* The boot process is:
@@ -149,17 +256,55 @@ typedef struct VbKernelPreambleHeader {
* For example, via ACPI or ATAGs. */
typedef struct VbSharedDataHeader {
/* Fields present in version 1 */
+ uint32_t magic; /* Magic number for struct
+ * (VB_SHARED_DATA_MAGIC) */
uint32_t struct_version; /* Version of this structure */
uint64_t struct_size; /* Size of this structure in bytes */
uint64_t data_size; /* Size of shared data buffer in bytes */
uint64_t data_used; /* Amount of shared data used so far */
+ uint32_t flags; /* Flags */
+ uint32_t reserved0; /* Reserved for padding */
VbPublicKey kernel_subkey; /* Kernel subkey, from firmware */
uint64_t kernel_subkey_data_offset; /* Offset of kernel subkey data from
* start of this struct */
- uint64_t kernel_subkey_data_size; /* Offset of kernel subkey data */
-
- uint64_t flags; /* Flags */
+ uint64_t kernel_subkey_data_size; /* Size of kernel subkey data */
+
+ /* Timer values from VbGetTimer(). Unused values are set to 0. If a
+ * function is called mutiple times, these are the times from the
+ * most recent call. */
+ uint64_t timer_load_firmware_start_enter; /* LoadFirmwareStart() - enter */
+ uint64_t timer_load_firmware_start_exit; /* LoadFirmwareStart() - exit */
+ uint64_t timer_load_firmware_enter; /* LoadFirmware() - enter */
+ uint64_t timer_load_firmware_exit; /* LoadFirmware() - exit */
+ uint64_t timer_load_kernel_enter; /* LoadKernel() - enter */
+ uint64_t timer_load_kernel_exit; /* LoadKernel() - exit */
+
+ /* Information stored in TPM, as retrieved by firmware */
+ uint32_t fw_version_tpm; /* Current firmware version in TPM */
+ uint32_t kernel_version_tpm; /* Current kernel version in TPM */
+
+ /* Debugging information from LoadFirmware() */
+ uint8_t check_fw_a_result; /* Result of checking RW firmware A */
+ uint8_t check_fw_b_result; /* Result of checking RW firmware B */
+ uint8_t firmware_index; /* Firmware index returned by
+ * LoadFirmware() or 0xFF if failure */
+ uint8_t reserved1; /* Reserved for padding */
+ uint32_t fw_version_tpm_start; /* Firmware TPM version at start of
+ * LoadFirmware() */
+ uint32_t fw_version_lowest; /* Firmware lowest version found */
+
+ /* Debugging information from LoadKernel() */
+ uint32_t lk_call_count; /* Number of times LoadKernel() called */
+ VbSharedDataKernelCall lk_calls[VBSD_MAX_KERNEL_CALLS]; /* Info on calls */
+
+ /* Offset and size of supplemental kernel data. Reserve space for these
+ * fields now, so that future LoadKernel() versions can store information
+ * there without needing to shift down whatever data the original
+ * LoadFirmware() might have put immediately following its
+ * VbSharedDataHeader. */
+ uint64_t kernel_supplemental_offset;
+ uint64_t kernel_supplemental_size;
/* After read-only firmware which uses version 1 is released, any additional
* fields must be added below, and the struct version must be increased.
diff --git a/firmware/lib/include/rollback_index.h b/firmware/lib/include/rollback_index.h
index 0e630dbb..7db85150 100644
--- a/firmware/lib/include/rollback_index.h
+++ b/firmware/lib/include/rollback_index.h
@@ -94,6 +94,11 @@ uint32_t RollbackS3Resume(void);
* mode. */
uint32_t RollbackFirmwareSetup(int developer_mode, uint32_t* version);
+/* Read may be called to get the version. This is not necessary in
+ * the normal boot path, because RollbackFirmwareSetup() provides the
+ * same information. It may be used in the recovery path. */
+uint32_t RollbackFirmwareRead(uint32_t* version);
+
/* Write may be called if the versions change */
uint32_t RollbackFirmwareWrite(uint32_t version);
@@ -109,8 +114,7 @@ uint32_t RollbackFirmwareLock(void);
* mode. */
uint32_t RollbackKernelRecovery(int developer_mode);
-/* Read and write may be called if not in developer mode. If called in
- * recovery mode, the effect is undefined. */
+/* Read and write may be called to read and write the kernel version. */
uint32_t RollbackKernelRead(uint32_t* version);
uint32_t RollbackKernelWrite(uint32_t version);
diff --git a/firmware/lib/include/tpm_bootmode.h b/firmware/lib/include/tpm_bootmode.h
new file mode 100644
index 00000000..6213cfe7
--- /dev/null
+++ b/firmware/lib/include/tpm_bootmode.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Functions for updating the TPM state with the status of boot path.
+ */
+
+#ifndef VBOOT_REFERENCE_TPM_BOOTMODE_H_
+#define VBOOT_REFERENCE_TPM_BOOTMODE_H_
+
+#include "sysincludes.h"
+
+/* Update TPM PCR State with the boot path status.
+ * [developer_mode]: State of the developer switch.
+ * [recovery_mode}: State of the recovery mode.
+ * [fw_keyblock_flags]: Keyblock flags on the to-be-booted
+ * RW firmware keyblock.
+ *
+ * Returns: TPM_SUCCESS if the TPM extend operation succeeds.
+ */
+
+uint32_t SetTPMBootModeState(int developer_mode, int recovery_mode,
+ int fw_keyblock_flags);
+
+#endif /* VBOOT_REFERENCE_TPM_BOOTMODE_H_ */
diff --git a/firmware/lib/rollback_index.c b/firmware/lib/rollback_index.c
index 0d9dc8c7..697bd0e7 100644
--- a/firmware/lib/rollback_index.c
+++ b/firmware/lib/rollback_index.c
@@ -9,28 +9,10 @@
#include "rollback_index.h"
#include "tlcl.h"
+#include "tpm_bootmode.h"
#include "tss_constants.h"
#include "utility.h"
-/* TPM PCR to use for storing dev mode measurements */
-#define DEV_REC_MODE_PCR 0
-/* Input digests for PCR extend */
-#define DEV_OFF_REC_OFF_SHA1_DIGEST ((uint8_t*) "\x14\x89\xf9\x23\xc4\xdc\xa7" \
- "\x29\x17\x8b\x3e\x32\x33\x45\x85\x50" \
- "\xd8\xdd\xdf\x29") /* SHA1("\x00\x00") */
-
-#define DEV_OFF_REC_ON_SHA1_DIGEST ((uint8_t*) "\x3f\x29\x54\x64\x53\x67\x8b" \
- "\x85\x59\x31\xc1\x74\xa9\x7d\x6c\x08" \
- "\x94\xb8\xf5\x46") /* SHA1("\x00\x01") */
-
-#define DEV_ON_REC_OFF_SHA1_DIGEST ((uint8_t*) "\x0e\x35\x6b\xa5\x05\x63\x1f" \
- "\xbf\x71\x57\x58\xbe\xd2\x7d\x50\x3f" \
- "\x8b\x26\x0e\x3a") /* SHA1("\x01\x00") */
-
-#define DEV_ON_REC_ON_SHA1_DIGEST ((uint8_t*) "\x91\x59\xcb\x8b\xce\xe7\xfc" \
- "\xb9\x55\x82\xf1\x40\x96\x0c\xda\xe7" \
- "\x27\x88\xd3\x26") /* SHA1("\x01\x01") */
-
static int g_rollback_recovery_mode = 0;
/* disable MSVC warning on const logical expression (as in } while(0);) */
@@ -121,6 +103,15 @@ static uint32_t OneTimeInitializeTPM(RollbackSpaceFirmware* rsf,
VBDEBUG(("TPM: One-time initialization\n"));
+ /* Do a full test. This only happens the first time the device is turned on
+ * in the factory, so performance is not an issue. This is almost certainly
+ * not necessary, but it gives us more confidence about some code paths below
+ * that are difficult to test---specifically the ones that set lifetime
+ * flags, and are only executed once per physical TPM. */
+ result = TlclSelfTestFull();
+ if (result != TPM_SUCCESS)
+ return result;
+
result = TlclGetPermanentFlags(&pflags);
if (result != TPM_SUCCESS)
return result;
@@ -200,12 +191,24 @@ uint32_t SetupTPM(int recovery_mode, int developer_mode,
RETURN_ON_FAILURE(TlclLibInit());
RETURN_ON_FAILURE(TlclStartup());
- /* Use ContinueSelfTest rather than SelfTestFull(). It enables
- * access to the subset of TPM commands we need in the firmware, and
- * allows the full self test to run in paralle with firmware
- * startup. By the time we get to the OS, self test will have
- * completed. */
+ /* Some TPMs start the self test automatically at power on. In that case we
+ * don't need to call ContinueSelfTest. On some (other) TPMs,
+ * ContinueSelfTest may block. In that case, we definitely don't want to
+ * call it here. For TPMs in the intersection of these two sets, we're
+ * screwed. (In other words: TPMs that require manually starting the
+ * self-test AND block will have poor performance until we split
+ * TlclSendReceive() into Send() and Receive(), and have a state machine to
+ * control setup.)
+ *
+ * This comment is likely to become obsolete in the near future, so don't
+ * trust it. It may have not been updated.
+ */
+#ifdef TPM_MANUAL_SELFTEST
+#ifdef TPM_BLOCKING_CONTINUESELFTEST
+#warning "lousy TPM!"
+#endif
RETURN_ON_FAILURE(TlclContinueSelfTest());
+#endif
result = TlclAssertPhysicalPresence();
if (result != 0) {
/* It is possible that the TPM was delivered with the physical presence
@@ -294,7 +297,11 @@ uint32_t RollbackFirmwareSetup(int developer_mode, uint32_t* version) {
TlclStartup();
TlclContinueSelfTest();
#endif
+ *version = 0;
+ return TPM_SUCCESS;
+}
+uint32_t RollbackFirmwareRead(uint32_t* version) {
*version = 0;
return TPM_SUCCESS;
}
@@ -348,20 +355,21 @@ uint32_t RollbackS3Resume(void) {
uint32_t RollbackFirmwareSetup(int developer_mode, uint32_t* version) {
RollbackSpaceFirmware rsf;
- uint8_t out_digest[20]; /* For PCR extend output */
RETURN_ON_FAILURE(SetupTPM(0, developer_mode, &rsf));
*version = rsf.fw_versions;
VBDEBUG(("TPM: RollbackFirmwareSetup %x\n", (int)rsf.fw_versions));
- if (developer_mode)
- RETURN_ON_FAILURE(TlclExtend(DEV_REC_MODE_PCR, DEV_ON_REC_OFF_SHA1_DIGEST,
- out_digest));
- else
- RETURN_ON_FAILURE(TlclExtend(DEV_REC_MODE_PCR, DEV_OFF_REC_OFF_SHA1_DIGEST,
- out_digest));
- VBDEBUG(("TPM: RollbackFirmwareSetup dev mode PCR out_digest %02x %02x %02x "
- "%02x\n", out_digest, out_digest+1, out_digest+2, out_digest+3));
+ return TPM_SUCCESS;
+}
+
+uint32_t RollbackFirmwareRead(uint32_t* version) {
+ RollbackSpaceFirmware rsf;
+ RETURN_ON_FAILURE(ReadSpaceFirmware(&rsf));
+ VBDEBUG(("TPM: RollbackFirmwareRead %x --> %x\n", (int)rsf.fw_versions,
+ (int)version));
+ *version = rsf.fw_versions;
+ VBDEBUG(("TPM: RollbackFirmwareRead %x\n", (int)rsf.fw_versions));
return TPM_SUCCESS;
}
@@ -382,7 +390,6 @@ uint32_t RollbackFirmwareLock(void) {
uint32_t RollbackKernelRecovery(int developer_mode) {
uint32_t rvs, rve;
RollbackSpaceFirmware rsf;
- uint8_t out_digest[20]; /* For PCR extend output */
/* In recovery mode we ignore TPM malfunctions or corruptions, and *
* leave the TPM complelely unlocked; we call neither
@@ -390,50 +397,40 @@ uint32_t RollbackKernelRecovery(int developer_mode) {
* kernel will fix the TPM (if needed) and lock it ASAP. We leave
* Physical Presence on in either case. */
rvs = SetupTPM(1, developer_mode, &rsf);
- if (developer_mode)
- rve = TlclExtend(DEV_REC_MODE_PCR, DEV_ON_REC_ON_SHA1_DIGEST, out_digest);
- else
- rve = TlclExtend(DEV_REC_MODE_PCR, DEV_OFF_REC_ON_SHA1_DIGEST, out_digest);
- VBDEBUG(("TPM: RollbackKernelRecovery dev mode PCR out_digest %02x %02x %02x "
- "%02x\n", out_digest, out_digest+1, out_digest+2, out_digest+3));
+ rve = SetTPMBootModeState(developer_mode,
+ 1, /* Recovery Mode Status. */
+ 0); /* In recovery mode, there is no RW firmware
+ * keyblock flag. */
return (TPM_SUCCESS == rvs) ? rve : rvs;
}
uint32_t RollbackKernelRead(uint32_t* version) {
- if (g_rollback_recovery_mode) {
- *version = 0;
- } else {
- RollbackSpaceKernel rsk;
- uint32_t perms;
-
- /* Read the kernel space and verify its permissions. If the kernel
- * space has the wrong permission, or it doesn't contain the right
- * identifier, we give up. This will need to be fixed by the
- * recovery kernel. We have to worry about this because at any time
- * (even with PP turned off) the TPM owner can remove and redefine a
- * PP-protected space (but not write to it). */
- RETURN_ON_FAILURE(ReadSpaceKernel(&rsk));
- RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_NV_INDEX, &perms));
- if (TPM_NV_PER_PPWRITE != perms || ROLLBACK_SPACE_KERNEL_UID != rsk.uid)
- return TPM_E_CORRUPTED_STATE;
-
- *version = rsk.kernel_versions;
- VBDEBUG(("TPM: RollbackKernelRead %x\n", (int)rsk.kernel_versions));
- }
+ RollbackSpaceKernel rsk;
+ uint32_t perms;
+
+ /* Read the kernel space and verify its permissions. If the kernel
+ * space has the wrong permission, or it doesn't contain the right
+ * identifier, we give up. This will need to be fixed by the
+ * recovery kernel. We have to worry about this because at any time
+ * (even with PP turned off) the TPM owner can remove and redefine a
+ * PP-protected space (but not write to it). */
+ RETURN_ON_FAILURE(ReadSpaceKernel(&rsk));
+ RETURN_ON_FAILURE(TlclGetPermissions(KERNEL_NV_INDEX, &perms));
+ if (TPM_NV_PER_PPWRITE != perms || ROLLBACK_SPACE_KERNEL_UID != rsk.uid)
+ return TPM_E_CORRUPTED_STATE;
+
+ *version = rsk.kernel_versions;
+ VBDEBUG(("TPM: RollbackKernelRead %x\n", (int)rsk.kernel_versions));
return TPM_SUCCESS;
}
uint32_t RollbackKernelWrite(uint32_t version) {
- if (g_rollback_recovery_mode) {
- return TPM_SUCCESS;
- } else {
- RollbackSpaceKernel rsk;
- RETURN_ON_FAILURE(ReadSpaceKernel(&rsk));
- VBDEBUG(("TPM: RollbackKernelWrite %x --> %x\n", (int)rsk.kernel_versions,
- (int)version));
- rsk.kernel_versions = version;
- return WriteSpaceKernel(&rsk);
- }
+ RollbackSpaceKernel rsk;
+ RETURN_ON_FAILURE(ReadSpaceKernel(&rsk));
+ VBDEBUG(("TPM: RollbackKernelWrite %x --> %x\n", (int)rsk.kernel_versions,
+ (int)version));
+ rsk.kernel_versions = version;
+ return WriteSpaceKernel(&rsk);
}
uint32_t RollbackKernelLock(void) {
diff --git a/firmware/lib/tpm_bootmode.c b/firmware/lib/tpm_bootmode.c
new file mode 100644
index 00000000..13371c40
--- /dev/null
+++ b/firmware/lib/tpm_bootmode.c
@@ -0,0 +1,134 @@
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Functions for updating the TPM state with the status of boot path.
+ */
+
+#include "tpm_bootmode.h"
+
+#include "tlcl.h"
+#include "utility.h"
+
+/* TPM PCR to use for storing boot mode measurements. */
+#define BOOT_MODE_PCR 0
+
+/* Input digests for PCR extend.
+ * These are calculated as:
+ * SHA1("|Developer_Mode||Recovery_Mode||Keyblock_Mode|").
+ * Developer_Mode can be 0 or 1.
+ * Recovery_Mode can be 0 or 1.
+ * Keyblock flags are defined in vboot_struct.h
+ *
+ * We map them to Keyblock_Mode as follows:
+ * -----------------------------------------
+ * Keyblock Flags | Keyblock Mode
+ * -----------------------------------------
+ * 6 (Dev-signed firmware) | 2
+ * 7 Normal-signed firmware | 1
+ * (anything else) | 0
+ */
+
+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\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\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\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\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\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\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*))
+
+/* 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"
+};
+
+/* Given the boot state, return the correct SHA1 digest index for TPMExtend
+ * in kBootStateSHA1Digests[]. */
+int GetBootStateIndex(int dev_mode, int rec_mode, int 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;
+}
+
+uint32_t SetTPMBootModeState(int developer_mode, int recovery_mode,
+ int 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;
+}
diff --git a/firmware/lib/tpm_lite/include/tlcl_structures.h b/firmware/lib/tpm_lite/include/tlcl_structures.h
index 53e70aed..a53e2baa 100644
--- a/firmware/lib/tpm_lite/include/tlcl_structures.h
+++ b/firmware/lib/tpm_lite/include/tlcl_structures.h
@@ -64,6 +64,11 @@ const struct s_tpm_resume_cmd{
} tpm_resume_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x2, },
};
+const struct s_tpm_savestate_cmd{
+ uint8_t buffer[10];
+} tpm_savestate_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xa, 0x0, 0x0, 0x0, 0x98, },
+};
+
const struct s_tpm_startup_cmd{
uint8_t buffer[12];
} tpm_startup_cmd = {{0x0, 0xc1, 0x0, 0x0, 0x0, 0xc, 0x0, 0x0, 0x0, 0x99, 0x0, 0x1, },
diff --git a/firmware/lib/tpm_lite/tlcl.c b/firmware/lib/tpm_lite/tlcl.c
index 12742d8e..f36f4633 100644
--- a/firmware/lib/tpm_lite/tlcl.c
+++ b/firmware/lib/tpm_lite/tlcl.c
@@ -44,11 +44,11 @@ static INLINE int TpmReturnCode(const uint8_t* buffer) {
return TpmCommandCode(buffer);
}
-/* Sends a TPM command and gets a response. Returns 0 if success or the TPM
- * error code if error. */
-static uint32_t TlclSendReceive(const uint8_t* request, uint8_t* response,
- int max_length) {
-
+/* Like TlclSendReceive below, but do not retry if NEEDS_SELFTEST or
+ * DOING_SELFTEST errors are returned.
+ */
+static uint32_t TlclSendReceiveNoRetry(const uint8_t* request,
+ uint8_t* response, int max_length) {
uint32_t result;
#ifdef EXTRA_LOGGING
@@ -83,6 +83,40 @@ static uint32_t TlclSendReceive(const uint8_t* request, uint8_t* response,
}
+/* Sends a TPM command and gets a response. Returns 0 if success or the TPM
+ * error code if error. In the firmware, waits for the self test to complete
+ * if needed. In the host, reports the first error without retries. */
+static uint32_t TlclSendReceive(const uint8_t* request, uint8_t* response,
+ int max_length) {
+ uint32_t result = TlclSendReceiveNoRetry(request, response, max_length);
+ /* When compiling for the firmware, hide command failures due to the self
+ * test not having run or completed. */
+#ifndef CHROMEOS_ENVIRONMENT
+ /* If the command fails because the self test has not completed, try it
+ * again after attempting to ensure that the self test has completed. */
+ if (result == TPM_E_NEEDS_SELFTEST || result == TPM_E_DOING_SELFTEST) {
+ result = TlclContinueSelfTest();
+ if (result != TPM_SUCCESS) {
+ return result;
+ }
+#if defined(TPM_BLOCKING_CONTINUESELFTEST) || defined(VB_RECOVERY_MODE)
+ /* Retry only once */
+ result = TlclSendReceiveNoRetry(request, response, max_length);
+#else
+ /* This needs serious testing. The TPM specification says: "iii. The
+ * caller MUST wait for the actions of TPM_ContinueSelfTest to complete
+ * before reissuing the command C1." But, if ContinueSelfTest is
+ * non-blocking, how do we know that the actions have completed other than
+ * trying again? */
+ do {
+ result = TlclSendReceiveNoRetry(request, response, max_length);
+ } while (result == TPM_E_DOING_SELFTEST);
+#endif
+ }
+#endif /* ! defined(CHROMEOS_ENVIRONMENT) */
+ return result;
+}
+
/* Sends a command and returns the error code. */
static uint32_t Send(const uint8_t* command) {
uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
@@ -100,6 +134,11 @@ uint32_t TlclStartup(void) {
return Send(tpm_startup_cmd.buffer);
}
+uint32_t TlclSaveState(void) {
+ VBDEBUG(("TPM: SaveState\n"));
+ return Send(tpm_savestate_cmd.buffer);
+}
+
uint32_t TlclResume(void) {
VBDEBUG(("TPM: Resume\n"));
return Send(tpm_resume_cmd.buffer);
@@ -111,8 +150,11 @@ uint32_t TlclSelfTestFull(void) {
}
uint32_t TlclContinueSelfTest(void) {
+ uint8_t response[TPM_LARGE_ENOUGH_COMMAND_SIZE];
VBDEBUG(("TPM: Continue self test\n"));
- return Send(tpm_continueselftest_cmd.buffer);
+ /* Call the No Retry version of SendReceive to avoid recursion. */
+ return TlclSendReceiveNoRetry(tpm_continueselftest_cmd.buffer,
+ response, sizeof(response));
}
uint32_t TlclDefineSpace(uint32_t index, uint32_t perm, uint32_t size) {
@@ -291,7 +333,8 @@ uint32_t TlclSetGlobalLock(void) {
return TlclWrite(TPM_NV_INDEX0, (uint8_t*) &x, 0);
}
-uint32_t TlclExtend(int pcr_num, uint8_t* in_digest, uint8_t* out_digest) {
+uint32_t TlclExtend(int pcr_num, const uint8_t* in_digest,
+ uint8_t* out_digest) {
struct s_tpm_extend_cmd cmd;
uint8_t response[kTpmResponseHeaderLength + kPcrDigestLength];
uint32_t result;
diff --git a/firmware/lib/vboot_common.c b/firmware/lib/vboot_common.c
index abba130b..edc6e163 100644
--- a/firmware/lib/vboot_common.c
+++ b/firmware/lib/vboot_common.c
@@ -381,6 +381,10 @@ int VerifyKernelPreamble(const VbKernelPreambleHeader* preamble,
int VbSharedDataInit(VbSharedDataHeader* header, uint64_t size) {
+
+ VBDEBUG(("VbSharedDataInit, %d bytes, header %d bytes\n", (int)size,
+ sizeof(VbSharedDataHeader)));
+
if (size < sizeof(VbSharedDataHeader)) {
VBDEBUG(("Not enough data for header.\n"));
return VBOOT_SHARED_DATA_INVALID;
@@ -397,10 +401,12 @@ int VbSharedDataInit(VbSharedDataHeader* header, uint64_t size) {
Memset(header, 0, sizeof(VbSharedDataHeader));
/* Initialize fields */
+ header->magic = VB_SHARED_DATA_MAGIC;
header->struct_version = VB_SHARED_DATA_VERSION;
header->struct_size = sizeof(VbSharedDataHeader);
header->data_size = size;
header->data_used = sizeof(VbSharedDataHeader);
+ header->firmware_index = 0xFF;
/* Success */
return VBOOT_SUCCESS;
@@ -410,6 +416,8 @@ int VbSharedDataInit(VbSharedDataHeader* header, uint64_t size) {
uint64_t VbSharedDataReserve(VbSharedDataHeader* header, uint64_t size) {
uint64_t offs = header->data_used;
+ VBDEBUG(("VbSharedDataReserve %d bytes at %d\n", (int)size, (int)offs));
+
if (!header || size > header->data_size - header->data_used) {
VBDEBUG(("VbSharedData buffer out of space.\n"));
return 0; /* Not initialized, or not enough space left. */
diff --git a/firmware/lib/vboot_firmware.c b/firmware/lib/vboot_firmware.c
index 2f358522..4be4cb29 100644
--- a/firmware/lib/vboot_firmware.c
+++ b/firmware/lib/vboot_firmware.c
@@ -9,6 +9,7 @@
#include "gbb_header.h"
#include "load_firmware_fw.h"
#include "rollback_index.h"
+#include "tpm_bootmode.h"
#include "utility.h"
#include "vboot_common.h"
#include "vboot_nvstorage.h"
@@ -34,6 +35,8 @@ void UpdateFirmwareBodyHash(LoadFirmwareParams* params,
int LoadFirmwareSetup(void) {
+ /* TODO: handle test errors (requires passing in VbNvContext) */
+ /* TODO: record timer values (requires passing in VbSharedData) */
/* TODO: start initializing the TPM */
return LOAD_FIRMWARE_SUCCESS;
}
@@ -50,7 +53,9 @@ int LoadFirmware(LoadFirmwareParams* params) {
uint32_t tpm_version = 0;
uint64_t lowest_version = 0xFFFFFFFF;
uint32_t status;
+ uint32_t test_err = 0;
int good_index = -1;
+ uint64_t boot_fw_keyblock_flags = 0;
int is_dev;
int index;
int i;
@@ -72,6 +77,28 @@ int LoadFirmware(LoadFirmwareParams* params) {
recovery = VBNV_RECOVERY_RO_SHARED_DATA;
goto LoadFirmwareExit;
}
+ shared->timer_load_firmware_enter = VbGetTimer();
+
+ /* Handle test errors */
+ VbNvGet(vnc, VBNV_TEST_ERROR_FUNC, &test_err);
+ if (VBNV_TEST_ERROR_LOAD_FIRMWARE == test_err) {
+ /* Get error code */
+ VbNvGet(vnc, VBNV_TEST_ERROR_NUM, &test_err);
+ /* Clear test params so we don't repeat the error */
+ VbNvSet(vnc, VBNV_TEST_ERROR_FUNC, 0);
+ VbNvSet(vnc, VBNV_TEST_ERROR_NUM, 0);
+ /* Handle error codes */
+ switch (test_err) {
+ case LOAD_FIRMWARE_RECOVERY:
+ recovery = VBNV_RECOVERY_RO_TEST_LF;
+ goto LoadFirmwareExit;
+ case LOAD_FIRMWARE_REBOOT:
+ retval = test_err;
+ goto LoadFirmwareExit;
+ default:
+ break;
+ }
+ }
/* Must have a root key from the GBB */
if (!gbb) {
@@ -82,6 +109,8 @@ int LoadFirmware(LoadFirmwareParams* params) {
/* Parse flags */
is_dev = (params->boot_flags & BOOT_FLAG_DEVELOPER ? 1 : 0);
+ if (is_dev)
+ shared->flags |= VBSD_LF_DEV_SWITCH_ON;
/* Initialize the TPM and read rollback indices. */
VBPERFSTART("VB_TPMI");
@@ -95,13 +124,16 @@ int LoadFirmware(LoadFirmwareParams* params) {
recovery = VBNV_RECOVERY_RO_TPM_ERROR;
goto LoadFirmwareExit;
}
+ shared->fw_version_tpm_start = tpm_version;
+ shared->fw_version_tpm = tpm_version;
VBPERFEND("VB_TPMI");
/* Read try-b count and decrement if necessary */
VbNvGet(vnc, VBNV_TRY_B_COUNT, &try_b_count);
- if (0 != try_b_count)
+ if (0 != try_b_count) {
VbNvSet(vnc, VBNV_TRY_B_COUNT, try_b_count - 1);
- VbNvSet(vnc, VBNV_TRIED_FIRMWARE_B, try_b_count ? 1 : 0);
+ shared->flags |= VBSD_FWB_TRIED;
+ }
/* Allocate our internal data */
lfi = (VbLoadFirmwareInternal*)Malloc(sizeof(VbLoadFirmwareInternal));
@@ -119,15 +151,18 @@ int LoadFirmware(LoadFirmwareParams* params) {
uint64_t key_version;
uint64_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*)params->verification_block_0;
vblock_size = params->verification_size_0;
+ check_result = &shared->check_fw_a_result;
} else {
key_block = (VbKeyBlockHeader*)params->verification_block_1;
vblock_size = params->verification_size_1;
+ check_result = &shared->check_fw_b_result;
}
/* Check the key block flags against the current boot mode. Do this
@@ -137,11 +172,13 @@ int LoadFirmware(LoadFirmwareParams* params) {
(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;
}
@@ -149,6 +186,7 @@ int LoadFirmware(LoadFirmwareParams* params) {
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;
}
@@ -158,6 +196,7 @@ int LoadFirmware(LoadFirmwareParams* params) {
key_version = key_block->data_key.key_version;
if (key_version < (tpm_version >> 16)) {
VBDEBUG(("Key rollback detected.\n"));
+ *check_result = VBSD_LF_CHECK_KEY_ROLLBACK;
continue;
}
@@ -165,6 +204,7 @@ int LoadFirmware(LoadFirmwareParams* params) {
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;
}
@@ -176,6 +216,7 @@ int LoadFirmware(LoadFirmwareParams* params) {
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;
@@ -187,10 +228,14 @@ int LoadFirmware(LoadFirmwareParams* params) {
(preamble->firmware_version & 0xFFFF));
if (combined_version < tpm_version) {
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;
@@ -207,6 +252,7 @@ int LoadFirmware(LoadFirmwareParams* params) {
lfi->body_size_accum = 0;
if (0 != GetFirmwareBody(params, index)) {
VBDEBUG(("GetFirmwareBody() failed for index %d\n", index));
+ *check_result = VBSD_LF_CHECK_GET_FW_BODY;
RSAPublicKeyFree(data_key);
VBPERFEND("VB_RFD");
continue;
@@ -215,6 +261,7 @@ int LoadFirmware(LoadFirmwareParams* params) {
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;
@@ -226,6 +273,7 @@ int LoadFirmware(LoadFirmwareParams* params) {
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);
Free(body_digest);
VBPERFEND("VB_VFD");
@@ -239,6 +287,7 @@ int LoadFirmware(LoadFirmwareParams* params) {
/* 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)) {
@@ -251,6 +300,9 @@ int LoadFirmware(LoadFirmwareParams* params) {
* this firmware. That's the one we'll boot. */
good_index = index;
params->firmware_index = index;
+ /* Since we now know which firmware to boot, we can update the
+ * bootable firmware key block mode. */
+ boot_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.
@@ -261,6 +313,19 @@ int LoadFirmware(LoadFirmwareParams* params) {
}
}
+ /* At this point, we have a good idea of how we are going to boot. Update the
+ * TPM with this state information.
+ */
+ status = SetTPMBootModeState(is_dev, 0, (int)boot_fw_keyblock_flags);
+ if (0 != status) {
+ VBDEBUG(("Unable to update the TPM with boot mode information.\n"));
+ if (status == TPM_E_MUST_REBOOT)
+ retval = LOAD_FIRMWARE_REBOOT;
+ else
+ recovery = VBNV_RECOVERY_RO_TPM_ERROR;
+ goto LoadFirmwareExit;
+ }
+
/* Free internal data */
Free(lfi);
params->load_firmware_internal = NULL;
@@ -269,6 +334,7 @@ int LoadFirmware(LoadFirmwareParams* params) {
if (good_index >= 0) {
/* Update TPM if necessary */
+ shared->fw_version_lowest = (uint32_t)lowest_version;
if (lowest_version > tpm_version) {
VBPERFSTART("VB_TPMU");
status = RollbackFirmwareWrite((uint32_t)lowest_version);
@@ -281,6 +347,7 @@ int LoadFirmware(LoadFirmwareParams* params) {
recovery = VBNV_RECOVERY_RO_TPM_ERROR;
goto LoadFirmwareExit;
}
+ shared->fw_version_tpm = (uint32_t)lowest_version;
}
/* Lock firmware versions in TPM */
@@ -298,6 +365,7 @@ int LoadFirmware(LoadFirmwareParams* params) {
/* Success */
VBDEBUG(("Will boot firmware index %d\n", (int)params->firmware_index));
+ shared->firmware_index = (uint8_t)params->firmware_index;
retval = LOAD_FIRMWARE_SUCCESS;
} else {
/* No good firmware, so go to recovery mode. */
@@ -311,6 +379,8 @@ LoadFirmwareExit:
recovery : VBNV_RECOVERY_NOT_REQUESTED);
VbNvTeardown(vnc);
+ shared->timer_load_firmware_exit = VbGetTimer();
+
/* Note that we don't reduce params->shared_data_size to shared->data_used,
* since we want to leave space for LoadKernel() to add to the shared data
* buffer. */
@@ -320,6 +390,9 @@ LoadFirmwareExit:
int S3Resume(void) {
+
+ /* TODO: handle test errors (requires passing in VbNvContext) */
+
/* Resume the TPM */
uint32_t status = RollbackS3Resume();
diff --git a/firmware/lib/vboot_kernel.c b/firmware/lib/vboot_kernel.c
index 3d082679..cfdd9b4c 100644
--- a/firmware/lib/vboot_kernel.c
+++ b/firmware/lib/vboot_kernel.c
@@ -21,9 +21,9 @@
#define LOWEST_TPM_VERSION 0xffffffff
typedef enum BootMode {
- kBootNormal, /* Normal firmware */
- kBootDev, /* Dev firmware AND dev switch is on */
- kBootRecovery /* Recovery firmware, regardless of dev switch position */
+ kBootRecovery = 0, /* Recovery firmware, regardless of dev switch position */
+ kBootNormal = 1, /* Normal firmware */
+ kBootDev = 2 /* Dev firmware AND dev switch is on */
} BootMode;
@@ -121,6 +121,7 @@ __pragma(warning(disable: 4127))
int 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;
@@ -136,13 +137,12 @@ int LoadKernel(LoadKernelParams* params) {
uint64_t lowest_version = LOWEST_TPM_VERSION;
int rec_switch, dev_switch;
BootMode boot_mode;
+ uint32_t test_err = 0;
uint32_t status;
- /* TODO: differentiate between finding an invalid kernel (found_partitions>0)
- * and not finding one at all. Right now we treat them the same, and return
- * LOAD_KERNEL_INVALID for both. */
- int retval = LOAD_KERNEL_INVALID;
+ int retval = LOAD_KERNEL_RECOVERY;
int recovery = VBNV_RECOVERY_RO_UNSPECIFIED;
+ uint64_t timer_enter = VbGetTimer();
/* Setup NV storage */
VbNvSetup(vnc);
@@ -157,39 +157,24 @@ int LoadKernel(LoadKernelParams* params) {
goto LoadKernelExit;
}
- /* Initialization */
- blba = params->bytes_per_lba;
- kbuf_sectors = KBUF_SIZE / blba;
- if (0 == kbuf_sectors) {
- VBDEBUG(("LoadKernel() called with sector size > KBUF_SIZE\n"));
- 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 (BOOT_FLAG_DEV_FIRMWARE & params->boot_flags) {
- if (!dev_switch) {
- /* Dev firmware should be signed such that it never boots with the dev
- * switch is off; so something is terribly wrong. */
- VBDEBUG(("LoadKernel() called with dev firmware but dev switch off\n"));
- recovery = VBNV_RECOVERY_RW_DEV_MISMATCH;
- goto LoadKernelExit;
- }
+ else if (BOOT_FLAG_DEV_FIRMWARE & params->boot_flags)
boot_mode = kBootDev;
- } else {
+ else {
/* Normal firmware */
boot_mode = kBootNormal;
dev_switch = 0; /* Always do a fully verified boot */
}
- /* Clear output params in case we fail */
- params->partition_number = 0;
- params->bootloader_address = 0;
- params->bootloader_size = 0;
-
if (kBootRecovery == boot_mode) {
/* Initialize the shared data structure, since LoadFirmware() didn't do it
* for us. */
@@ -200,16 +185,82 @@ int LoadKernel(LoadKernelParams* params) {
params->shared_data_size = 0;
shared = NULL;
}
+ }
+
+ if (shared) {
+ /* 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++;
+ }
+ /* Handle test errors */
+ VbNvGet(vnc, VBNV_TEST_ERROR_FUNC, &test_err);
+ if (VBNV_TEST_ERROR_LOAD_KERNEL == test_err) {
+ /* Get error code */
+ VbNvGet(vnc, VBNV_TEST_ERROR_NUM, &test_err);
+ if (shcall)
+ shcall->test_error_num = (uint8_t)test_err;
+ /* Clear test params so we don't repeat the error */
+ VbNvSet(vnc, VBNV_TEST_ERROR_FUNC, 0);
+ VbNvSet(vnc, VBNV_TEST_ERROR_NUM, 0);
+ /* Handle error codes */
+ switch (test_err) {
+ case LOAD_KERNEL_RECOVERY:
+ recovery = VBNV_RECOVERY_RW_TEST_LK;
+ goto LoadKernelExit;
+ case LOAD_KERNEL_NOT_FOUND:
+ case LOAD_KERNEL_INVALID:
+ case LOAD_KERNEL_REBOOT:
+ retval = test_err;
+ goto LoadKernelExit;
+ default:
+ break;
+ }
+ }
+
+ /* Initialization */
+ blba = params->bytes_per_lba;
+ kbuf_sectors = KBUF_SIZE / blba;
+ if (0 == kbuf_sectors) {
+ VBDEBUG(("LoadKernel() called with sector size > KBUF_SIZE\n"));
+ goto LoadKernelExit;
+ }
+
+ if (kBootDev == boot_mode && !dev_switch) {
+ /* Dev firmware should be signed such that it never boots with the dev
+ * switch is off; so something is terribly wrong. */
+ VBDEBUG(("LoadKernel() called with dev firmware but dev switch off\n"));
+ if (shcall)
+ shcall->check_result = VBSD_LKC_CHECK_DEV_SWITCH_MISMATCH;
+ recovery = VBNV_RECOVERY_RW_DEV_MISMATCH;
+ goto LoadKernelExit;
+ }
+
+ if (kBootRecovery == boot_mode) {
/* Use the recovery key to verify the kernel */
kernel_subkey = (VbPublicKey*)((uint8_t*)gbb + gbb->recovery_key_offset);
/* Let the TPM know if we're in recovery mode */
if (0 != RollbackKernelRecovery(dev_switch)) {
VBDEBUG(("Error setting up TPM for recovery kernel\n"));
+ if (shcall)
+ shcall->flags |= VBSD_LK_FLAG_REC_TPM_INIT_ERROR;
/* Ignore return code, since we need to boot recovery mode to
* fix the TPM. */
}
+
+ /* Read the key indices from the TPM; ignore any errors */
+ if (shared) {
+ RollbackFirmwareRead(&shared->fw_version_tpm);
+ RollbackKernelRead(&shared->kernel_version_tpm);
+ }
} else {
/* Use the kernel subkey passed from LoadFirmware(). */
kernel_subkey = &shared->kernel_subkey;
@@ -225,6 +276,8 @@ int LoadKernel(LoadKernelParams* params) {
recovery = VBNV_RECOVERY_RW_TPM_ERROR;
goto LoadKernelExit;
}
+ if (shared)
+ shared->kernel_version_tpm = tpm_version;
}
do {
@@ -233,12 +286,16 @@ int LoadKernel(LoadKernelParams* params) {
gpt.drive_sectors = params->ending_lba + 1;
if (0 != AllocAndReadGptData(&gpt)) {
VBDEBUG(("Unable to read GPT data\n"));
+ if (shcall)
+ shcall->check_result = VBSD_LKC_CHECK_GPT_READ_ERROR;
break;
}
/* Initialize GPT library */
if (GPT_SUCCESS != GptInit(&gpt)) {
VBDEBUG(("Error parsing GPT\n"));
+ if (shcall)
+ shcall->check_result = VBSD_LKC_CHECK_GPT_PARSE_ERROR;
break;
}
@@ -249,6 +306,7 @@ int LoadKernel(LoadKernelParams* params) {
/* 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;
@@ -262,17 +320,35 @@ int LoadKernel(LoadKernelParams* params) {
VBDEBUG(("Found kernel entry at %" PRIu64 " size %" PRIu64 "\n",
part_start, part_size));
+ if (shcall) {
+ /* 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"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_TOO_SMALL;
goto bad_kernel;
}
if (0 != BootDeviceReadLBA(part_start, kbuf_sectors, kbuf)) {
VBDEBUG(("Unable to read start of partition.\n"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_READ_START;
goto bad_kernel;
}
@@ -280,6 +356,9 @@ int LoadKernel(LoadKernelParams* params) {
key_block = (VbKeyBlockHeader*)kbuf;
if (0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey, 0)) {
VBDEBUG(("Verifying key block signature failed.\n"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_KEY_BLOCK_SIG;
+
key_block_valid = 0;
/* If we're not in developer mode, this kernel is bad. */
@@ -290,6 +369,8 @@ int LoadKernel(LoadKernelParams* params) {
* block is valid. */
if (0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey, 1)) {
VBDEBUG(("Verifying key block hash failed.\n"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_KEY_BLOCK_HASH;
goto bad_kernel;
}
}
@@ -299,12 +380,16 @@ int LoadKernel(LoadKernelParams* params) {
(dev_switch ? KEY_BLOCK_FLAG_DEVELOPER_1 :
KEY_BLOCK_FLAG_DEVELOPER_0))) {
VBDEBUG(("Key block developer flag mismatch.\n"));
+ if (shpart)
+ 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"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_REC_MISMATCH;
key_block_valid = 0;
}
@@ -313,6 +398,8 @@ int LoadKernel(LoadKernelParams* params) {
if (kBootRecovery != boot_mode) {
if (key_version < (tpm_version >> 16)) {
VBDEBUG(("Key version too old.\n"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_KEY_ROLLBACK;
key_block_valid = 0;
}
}
@@ -327,6 +414,8 @@ int LoadKernel(LoadKernelParams* params) {
data_key = PublicKeyToRSA(&key_block->data_key);
if (!data_key) {
VBDEBUG(("Data key bad.\n"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_DATA_KEY_PARSE;
goto bad_kernel;
}
@@ -336,6 +425,8 @@ int LoadKernel(LoadKernelParams* params) {
KBUF_SIZE - key_block->key_block_size,
data_key))) {
VBDEBUG(("Preamble verification failed.\n"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_VERIFY_PREAMBLE;
goto bad_kernel;
}
@@ -343,9 +434,13 @@ int LoadKernel(LoadKernelParams* params) {
* rollback of the kernel version. */
combined_version = ((key_version << 16) |
(preamble->kernel_version & 0xFFFF));
+ if (shpart)
+ shpart->combined_version = (uint32_t)combined_version;
if (key_block_valid && kBootRecovery != boot_mode) {
if (combined_version < tpm_version) {
VBDEBUG(("Kernel version too low.\n"));
+ if (shpart)
+ 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;
@@ -353,6 +448,8 @@ int LoadKernel(LoadKernelParams* params) {
}
VBDEBUG(("Kernel preamble is good.\n"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_PREAMBLE_VALID;
/* Check for lowest version from a valid header. */
if (key_block_valid && lowest_version > combined_version)
@@ -372,6 +469,8 @@ int LoadKernel(LoadKernelParams* params) {
if ((preamble->body_load_address != (size_t)params->kernel_buffer) &&
!(params->boot_flags & BOOT_FLAG_SKIP_ADDR_CHECK)) {
VBDEBUG(("Wrong body load address.\n"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_BODY_ADDRESS;
goto bad_kernel;
}
@@ -379,6 +478,8 @@ int LoadKernel(LoadKernelParams* params) {
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"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_BODY_OFFSET;
goto bad_kernel;
}
body_offset_sectors = body_offset / blba;
@@ -387,12 +488,16 @@ int LoadKernel(LoadKernelParams* params) {
body_sectors = (preamble->body_signature.data_size + blba - 1) / blba;
if (body_sectors * blba > params->kernel_buffer_size) {
VBDEBUG(("Kernel body doesn't fit in memory.\n"));
+ if (shpart)
+ 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"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_BODY_EXCEEDS_PART;
goto bad_kernel;
}
@@ -403,6 +508,8 @@ int LoadKernel(LoadKernelParams* params) {
params->kernel_buffer)) {
VBDEBUG(("Unable to read kernel data.\n"));
VBPERFEND("VB_RKD");
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_READ_DATA;
goto bad_kernel;
}
VBPERFEND("VB_RKD");
@@ -412,6 +519,8 @@ int LoadKernel(LoadKernelParams* params) {
params->kernel_buffer_size,
&preamble->body_signature, data_key)) {
VBDEBUG(("Kernel data verification failed.\n"));
+ if (shpart)
+ shpart->check_result = VBSD_LKP_CHECK_VERIFY_DATA;
goto bad_kernel;
}
@@ -422,6 +531,12 @@ int LoadKernel(LoadKernelParams* params) {
/* 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"));
+ if (shpart) {
+ 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. */
@@ -478,6 +593,8 @@ int LoadKernel(LoadKernelParams* params) {
/* Handle finding a good partition */
if (good_partition >= 0) {
VBDEBUG(("Good_partition >= 0\n"));
+ if (shcall)
+ shcall->check_result = VBSD_LKC_CHECK_GOOD_PARTITION;
/* See if we need to update the TPM */
if (kBootRecovery != boot_mode && good_partition_key_block_valid) {
@@ -499,6 +616,8 @@ int LoadKernel(LoadKernelParams* params) {
recovery = VBNV_RECOVERY_RW_TPM_ERROR;
goto LoadKernelExit;
}
+ if (shared)
+ shared->kernel_version_tpm = (uint32_t)lowest_version;
}
}
@@ -518,21 +637,39 @@ int LoadKernel(LoadKernelParams* params) {
/* Success! */
retval = LOAD_KERNEL_SUCCESS;
+ } else {
+ if (shcall)
+ shcall->check_result = (found_partitions > 0
+ ? VBSD_LKC_CHECK_INVALID_PARTITIONS
+ : VBSD_LKC_CHECK_NO_PARTITIONS);
+
+ /* TODO: differentiate between finding an invalid kernel
+ * (found_partitions>0) and not finding one at all. Right now we
+ * treat them the same, and return LOAD_KERNEL_INVALID for both. */
+ retval = LOAD_KERNEL_INVALID;
}
LoadKernelExit:
- /* Save whether the good partition's key block was fully verified */
- VbNvSet(vnc, VBNV_FW_VERIFIED_KERNEL_KEY, good_partition_key_block_valid);
-
/* Store recovery request, if any, then tear down non-volatile storage */
VbNvSet(vnc, VBNV_RECOVERY_REQUEST, LOAD_KERNEL_RECOVERY == retval ?
recovery : VBNV_RECOVERY_NOT_REQUESTED);
VbNvTeardown(vnc);
- /* Store how much shared data we used, if any */
- if (shared)
+ if (shared) {
+ 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;
+
+ /* Save timer values */
+ shared->timer_load_kernel_enter = timer_enter;
+ shared->timer_load_kernel_exit = VbGetTimer();
+ /* 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 419a9fbc..575fcb98 100644
--- a/firmware/lib/vboot_nvstorage.c
+++ b/firmware/lib/vboot_nvstorage.c
@@ -27,8 +27,9 @@
#define LOCALIZATION_OFFSET 3
#define FIRMWARE_FLAGS_OFFSET 5
-#define FIRMWARE_TRIED_FIRMWARE_B 0x80
-#define FIRMWARE_FW_VERIFIED_KERNEL_KEY 0x40
+#define FIRMWARE_TEST_ERR_FUNC_MASK 0x38
+#define FIRMWARE_TEST_ERR_FUNC_SHIFT 3
+#define FIRMWARE_TEST_ERR_NUM_MASK 0x07
#define KERNEL_FIELD_OFFSET 11
#define CRC_OFFSET 15
@@ -125,13 +126,13 @@ int VbNvGet(VbNvContext* context, VbNvParam param, uint32_t* dest) {
| (raw[KERNEL_FIELD_OFFSET + 3] << 24));
return 0;
- case VBNV_TRIED_FIRMWARE_B:
- *dest = (raw[FIRMWARE_FLAGS_OFFSET] & FIRMWARE_TRIED_FIRMWARE_B ? 1 : 0);
+ case VBNV_TEST_ERROR_FUNC:
+ *dest = (raw[FIRMWARE_FLAGS_OFFSET] & FIRMWARE_TEST_ERR_FUNC_MASK)
+ >> FIRMWARE_TEST_ERR_FUNC_SHIFT;
return 0;
- case VBNV_FW_VERIFIED_KERNEL_KEY:
- *dest = (raw[FIRMWARE_FLAGS_OFFSET] & FIRMWARE_FW_VERIFIED_KERNEL_KEY ?
- 1 : 0);
+ case VBNV_TEST_ERROR_NUM:
+ *dest = raw[FIRMWARE_FLAGS_OFFSET] & FIRMWARE_TEST_ERR_NUM_MASK;
return 0;
default:
@@ -201,18 +202,15 @@ int VbNvSet(VbNvContext* context, VbNvParam param, uint32_t value) {
raw[KERNEL_FIELD_OFFSET + 3] = (uint8_t)(value >> 24);
break;
- case VBNV_TRIED_FIRMWARE_B:
- if (value)
- raw[FIRMWARE_FLAGS_OFFSET] |= FIRMWARE_TRIED_FIRMWARE_B;
- else
- raw[FIRMWARE_FLAGS_OFFSET] &= ~FIRMWARE_TRIED_FIRMWARE_B;
+ case VBNV_TEST_ERROR_FUNC:
+ raw[FIRMWARE_FLAGS_OFFSET] &= ~FIRMWARE_TEST_ERR_FUNC_MASK;
+ raw[FIRMWARE_FLAGS_OFFSET] |= (value << FIRMWARE_TEST_ERR_FUNC_SHIFT)
+ & FIRMWARE_TEST_ERR_FUNC_MASK;
break;
- case VBNV_FW_VERIFIED_KERNEL_KEY:
- if (value)
- raw[FIRMWARE_FLAGS_OFFSET] |= FIRMWARE_FW_VERIFIED_KERNEL_KEY;
- else
- raw[FIRMWARE_FLAGS_OFFSET] &= ~FIRMWARE_FW_VERIFIED_KERNEL_KEY;
+ case VBNV_TEST_ERROR_NUM:
+ raw[FIRMWARE_FLAGS_OFFSET] &= ~FIRMWARE_TEST_ERR_NUM_MASK;
+ raw[FIRMWARE_FLAGS_OFFSET] |= (value & FIRMWARE_TEST_ERR_NUM_MASK);
break;
default:
diff --git a/firmware/linktest/main.c b/firmware/linktest/main.c
index 6f5b83e4..a3ed21a5 100644
--- a/firmware/linktest/main.c
+++ b/firmware/linktest/main.c
@@ -8,6 +8,7 @@
#include "load_kernel_fw.h"
#include "rollback_index.h"
#include "tlcl.h"
+#include "tpm_bootmode.h"
#include "vboot_common.h"
#include "vboot_kernel.h"
#include "vboot_nvstorage.h"
@@ -30,6 +31,7 @@ int main(void)
/* rollback_index.h */
RollbackS3Resume();
RollbackFirmwareSetup(0, 0);
+ RollbackFirmwareRead(0);
RollbackFirmwareWrite(0);
RollbackFirmwareLock();
RollbackKernelRecovery(0);
@@ -37,6 +39,9 @@ int main(void)
RollbackKernelWrite(0);
RollbackKernelLock();
+ /* tpm_bootmode.c */
+ SetTPMBootModeState(0, 0, 0);
+
/* tlcl.h */
TlclLibInit();
TlclCloseDevice();
diff --git a/firmware/stub/load_firmware_stub.c b/firmware/stub/load_firmware_stub.c
index 0b0671aa..26ce3d50 100644
--- a/firmware/stub/load_firmware_stub.c
+++ b/firmware/stub/load_firmware_stub.c
@@ -99,8 +99,8 @@ int VerifyFirmwareDriver_stub(uint8_t* gbb_data,
p.nv_context = &vnc;
/* Allocate a shared data buffer */
- p.shared_data_blob = Malloc(LOAD_FIRMWARE_SHARED_DATA_REC_SIZE);
- p.shared_data_size = LOAD_FIRMWARE_SHARED_DATA_REC_SIZE;
+ p.shared_data_blob = Malloc(VB_SHARED_DATA_REC_SIZE);
+ p.shared_data_size = VB_SHARED_DATA_REC_SIZE;
/* TODO: YOU NEED TO SET THE BOOT FLAGS SOMEHOW */
p.boot_flags = 0;
diff --git a/firmware/stub/utility_stub.c b/firmware/stub/utility_stub.c
index 61417856..2d56f72b 100644
--- a/firmware/stub/utility_stub.c
+++ b/firmware/stub/utility_stub.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
@@ -13,6 +13,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/time.h>
void error(const char *format, ...) {
va_list ap;
@@ -51,3 +52,13 @@ int Memcmp(const void* src1, const void* src2, size_t n) {
void* Memcpy(void* dest, const void* src, uint64_t n) {
return memcpy(dest, src, (size_t)n);
}
+
+uint64_t VbGetTimer(void) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec;
+}
+
+uint64_t VbGetTimerMaxFreq(void) {
+ return UINT64_C(1000000);
+}
diff --git a/host/lib/crossystem.c b/host/lib/crossystem.c
index 04dc9756..5c5dec0d 100644
--- a/host/lib/crossystem.c
+++ b/host/lib/crossystem.c
@@ -5,6 +5,10 @@
#include <stdio.h>
#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
#include "host_common.h"
@@ -12,6 +16,7 @@
#include "utility.h"
#include "vboot_common.h"
#include "vboot_nvstorage.h"
+#include "vboot_struct.h"
/* ACPI constants from Chrome OS Main Processor Firmware Spec */
/* GPIO signal types */
@@ -68,6 +73,7 @@
#define ACPI_FMAP_PATH ACPI_BASE_PATH "/FMAP"
#define ACPI_GPIO_PATH ACPI_BASE_PATH "/GPIO"
#define ACPI_VBNV_PATH ACPI_BASE_PATH "/VBNV"
+#define ACPI_VDAT_PATH ACPI_BASE_PATH "/VDAT"
/* Base name for GPIO files */
#define GPIO_BASE_PATH "/sys/class/gpio"
@@ -79,6 +85,31 @@
/* Filename for kernel command line */
#define KERNEL_CMDLINE_PATH "/proc/cmdline"
+/* A structure to contain buffer data retrieved from the ACPI. */
+typedef struct {
+ int buffer_size;
+ uint8_t* buffer;
+} AcpiBuffer;
+
+
+/* Fields that GetVdatString() can get */
+typedef enum VdatStringField {
+ VDAT_STRING_TIMERS = 0, /* Timer values */
+ VDAT_STRING_LOAD_FIRMWARE_DEBUG, /* LoadFirmware() debug information */
+ VDAT_STRING_LOAD_KERNEL_DEBUG /* LoadKernel() debug information */
+} VdatStringField;
+
+
+/* Fields that GetVdatInt() can get */
+typedef enum VdatIntField {
+ VDAT_INT_FLAGS = 0, /* Flags */
+ VDAT_INT_FW_VERSION_TPM, /* Current firmware version in TPM */
+ VDAT_INT_KERNEL_VERSION_TPM, /* Current kernel version in TPM */
+ VDAT_INT_TRIED_FIRMWARE_B, /* Tried firmware B due to fwb_tries */
+ VDAT_INT_KERNEL_KEY_VERIFIED /* Kernel key verified using
+ * signature, not just hash */
+} VdatIntField;
+
/* Copy up to dest_size-1 characters from src to dest, ensuring null
termination (which strncpy() doesn't do). Returns the destination
@@ -284,6 +315,97 @@ int VbSetCmosRebootField(uint8_t mask, int value) {
return 0;
}
+/*
+ * Get buffer data from ACPI.
+ *
+ * Buffer data is expected to be represented by a file which is a text dump of
+ * the buffer, representing each byte by two hex numbers, space and newline
+ * separated.
+ *
+ * Input - ACPI file name to get data from.
+ *
+ * Output: a pointer to AcpiBuffer structure containing the binary
+ * representation of the data. The caller is responsible for
+ * deallocating the pointer, this will take care of both the structure
+ * and the buffer. Null in case of error.
+ */
+
+AcpiBuffer* VbGetBuffer(const char* filename)
+{
+ FILE* f = NULL;
+ char* file_buffer = NULL;
+ AcpiBuffer* acpi_buffer = NULL;
+ AcpiBuffer* return_value = NULL;
+
+ do {
+ struct stat fs;
+ uint8_t* output_ptr;
+ int rv, i, real_size;
+
+ rv = stat(filename, &fs);
+ if (rv || !S_ISREG(fs.st_mode))
+ break;
+
+ f = fopen(filename, "r");
+ if (!f)
+ break;
+
+ file_buffer = Malloc(fs.st_size + 1);
+ if (!file_buffer)
+ break;
+
+ real_size = fread(file_buffer, 1, fs.st_size, f);
+ if (!real_size)
+ break;
+ file_buffer[real_size] = '\0';
+
+ /* Each byte in the output will replace two characters and a space
+ * in the input, so the output size does not exceed input side/3
+ * (a little less if account for newline characters). */
+ acpi_buffer = Malloc(sizeof(AcpiBuffer) + real_size/3);
+ if (!acpi_buffer)
+ break;
+ acpi_buffer->buffer = (uint8_t*)(acpi_buffer + 1);
+ acpi_buffer->buffer_size = 0;
+ output_ptr = acpi_buffer->buffer;
+
+ /* process the file contents */
+ for (i = 0; i < real_size; i++) {
+ char* base, *end;
+
+ base = file_buffer + i;
+
+ if (!isxdigit(*base))
+ continue;
+
+ output_ptr[acpi_buffer->buffer_size++] = strtol(base, &end, 16) & 0xff;
+
+ if ((end - base) != 2)
+ /* Input file format error */
+ break;
+
+ i += 2; /* skip the second character and the following space */
+ }
+
+ if (i == real_size) {
+ /* all is well */
+ return_value = acpi_buffer;
+ acpi_buffer = NULL; /* prevent it from deallocating */
+ }
+ } while(0);
+
+ /* wrap up */
+ if (f)
+ fclose(f);
+
+ if (file_buffer)
+ Free(file_buffer);
+
+ if (acpi_buffer)
+ Free(acpi_buffer);
+
+ return return_value;
+}
/* Read an integer property from VbNvStorage.
*
@@ -497,6 +619,193 @@ int VbGetCrosDebug(void) {
}
+char* GetVdatLoadFirmwareDebug(char* dest, int size,
+ const VbSharedDataHeader* sh) {
+ snprintf(dest, size,
+ "Check A result=%d\n"
+ "Check B result=%d\n"
+ "Firmware index booted=0x%02x\n"
+ "TPM combined version at start=0x%08x\n"
+ "Lowest combined version from firmware=0x%08x\n",
+ sh->check_fw_a_result,
+ sh->check_fw_b_result,
+ sh->firmware_index,
+ sh->fw_version_tpm_start,
+ sh->fw_version_lowest);
+ return dest;
+}
+
+
+#define TRUNCATED "\n(truncated)\n"
+
+char* GetVdatLoadKernelDebug(char* dest, int size,
+ const VbSharedDataHeader* sh) {
+ int used = 0;
+ int first_call_tracked = 0;
+ int call;
+
+ /* Make sure we have space for truncation warning */
+ if (size < strlen(TRUNCATED) + 1)
+ return NULL;
+ size -= strlen(TRUNCATED) + 1;
+
+ used += snprintf(
+ dest + used, size - used,
+ "Calls to LoadKernel()=%d\n",
+ sh->lk_call_count);
+ if (used > size)
+ goto LoadKernelDebugExit;
+
+ /* Report on the last calls */
+ if (sh->lk_call_count > VBSD_MAX_KERNEL_CALLS)
+ first_call_tracked = sh->lk_call_count - VBSD_MAX_KERNEL_CALLS;
+ for (call = first_call_tracked; call < sh->lk_call_count; call++) {
+ const VbSharedDataKernelCall* shc =
+ sh->lk_calls + (call & (VBSD_MAX_KERNEL_CALLS - 1));
+ int first_part_tracked = 0;
+ int part;
+
+ used += snprintf(
+ dest + used, size - used,
+ "Call %d:\n"
+ " Boot flags=0x%02x\n"
+ " Boot mode=%d\n"
+ " Test error=%d\n"
+ " Return code=%d\n"
+ " Debug flags=0x%02x\n"
+ " Drive sectors=%" PRIu64 "\n"
+ " Sector size=%d\n"
+ " Check result=%d\n"
+ " Kernel partitions found=%d\n",
+ call + 1,
+ shc->boot_flags,
+ shc->boot_mode,
+ shc->test_error_num,
+ shc->return_code,
+ shc->flags,
+ shc->sector_count,
+ shc->sector_size,
+ shc->check_result,
+ shc->kernel_parts_found);
+ if (used > size)
+ goto LoadKernelDebugExit;
+
+ /* If we found too many partitions, only prints ones where the
+ * structure has info. */
+ if (shc->kernel_parts_found > VBSD_MAX_KERNEL_PARTS)
+ first_part_tracked = shc->kernel_parts_found - VBSD_MAX_KERNEL_PARTS;
+
+ /* Report on the partitions checked */
+ for (part = first_part_tracked; part < shc->kernel_parts_found; part++) {
+ const VbSharedDataKernelPart* shp =
+ shc->parts + (part & (VBSD_MAX_KERNEL_PARTS - 1));
+
+ used += snprintf(
+ dest + used, size - used,
+ " Kernel %d:\n"
+ " GPT index=%d\n"
+ " Start sector=%" PRIu64 "\n"
+ " Sector count=%" PRIu64 "\n"
+ " Combined version=0x%08x\n"
+ " Check result=%d\n"
+ " Debug flags=0x%02x\n",
+ part + 1,
+ shp->gpt_index,
+ shp->sector_start,
+ shp->sector_count,
+ shp->combined_version,
+ shp->check_result,
+ shp->flags);
+ if (used > size)
+ goto LoadKernelDebugExit;
+ }
+ }
+
+LoadKernelDebugExit:
+
+ /* Warn if data was truncated; we left space for this above. */
+ if (used > size)
+ strcat(dest, TRUNCATED);
+
+ return dest;
+}
+
+
+char* GetVdatString(char* dest, int size, VdatStringField field)
+{
+ VbSharedDataHeader* sh;
+ AcpiBuffer* ab = VbGetBuffer(ACPI_VDAT_PATH);
+ char* value = dest;
+ if (!ab)
+ return NULL;
+
+ sh = (VbSharedDataHeader*)ab->buffer;
+
+ switch (field) {
+ case VDAT_STRING_TIMERS:
+ snprintf(dest, size,
+ "LFS=%" PRIu64 ",%" PRIu64
+ " LF=%" PRIu64 ",%" PRIu64
+ " LK=%" PRIu64 ",%" PRIu64,
+ sh->timer_load_firmware_start_enter,
+ sh->timer_load_firmware_start_exit,
+ sh->timer_load_firmware_enter,
+ sh->timer_load_firmware_exit,
+ sh->timer_load_kernel_enter,
+ sh->timer_load_kernel_exit);
+ break;
+
+ case VDAT_STRING_LOAD_FIRMWARE_DEBUG:
+ value = GetVdatLoadFirmwareDebug(dest, size, sh);
+ break;
+
+ case VDAT_STRING_LOAD_KERNEL_DEBUG:
+ value = GetVdatLoadKernelDebug(dest, size, sh);
+ break;
+
+ default:
+ Free(ab);
+ return NULL;
+ }
+
+ Free(ab);
+ return value;
+}
+
+
+int GetVdatInt(VdatIntField field) {
+ VbSharedDataHeader* sh;
+ AcpiBuffer* ab = VbGetBuffer(ACPI_VDAT_PATH);
+ int value = -1;
+
+ if (!ab)
+ return -1;
+
+ sh = (VbSharedDataHeader*)ab->buffer;
+
+ switch (field) {
+ case VDAT_INT_FLAGS:
+ value = (int)sh->flags;
+ break;
+ case VDAT_INT_FW_VERSION_TPM:
+ value = (int)sh->fw_version_tpm;
+ break;
+ case VDAT_INT_KERNEL_VERSION_TPM:
+ value = (int)sh->kernel_version_tpm;
+ break;
+ case VDAT_INT_TRIED_FIRMWARE_B:
+ value = (sh->flags & VBSD_FWB_TRIED ? 1 : 0);
+ break;
+ case VDAT_INT_KERNEL_KEY_VERIFIED:
+ value = (sh->flags & VBSD_KERNEL_KEY_VERIFIED ? 1 : 0);
+ break;
+ }
+
+ Free(ab);
+ return value;
+}
+
+
/* Read a system property integer.
*
* Returns the property value, or -1 if error. */
@@ -531,12 +840,14 @@ int VbGetSystemPropertyInt(const char* name) {
return (-1 == ReadFileInt(ACPI_CHSW_PATH) ? -1 : 0x00100000);
}
/* NV storage values with no defaults for older BIOS. */
- else if (!strcasecmp(name,"tried_fwb")) {
- value = VbGetNvStorage(VBNV_TRIED_FIRMWARE_B);
- } else if (!strcasecmp(name,"kern_nv")) {
+ else if (!strcasecmp(name,"kern_nv")) {
value = VbGetNvStorage(VBNV_KERNEL_FIELD);
} else if (!strcasecmp(name,"nvram_cleared")) {
value = VbGetNvStorage(VBNV_KERNEL_SETTINGS_RESET);
+ } else if (!strcasecmp(name,"vbtest_errfunc")) {
+ value = VbGetNvStorage(VBNV_TEST_ERROR_FUNC);
+ } else if (!strcasecmp(name,"vbtest_errno")) {
+ value = VbGetNvStorage(VBNV_TEST_ERROR_NUM);
}
/* NV storage values. If unable to get from NV storage, fall back to the
* CMOS reboot field used by older BIOS. */
@@ -560,12 +871,19 @@ int VbGetSystemPropertyInt(const char* name) {
value = ReadFileInt(ACPI_FMAP_PATH);
} else if (!strcasecmp(name,"cros_debug")) {
value = VbGetCrosDebug();
+ } else if (!strcasecmp(name,"vdat_flags")) {
+ value = GetVdatInt(VDAT_INT_FLAGS);
+ } else if (!strcasecmp(name,"tpm_fwver")) {
+ value = GetVdatInt(VDAT_INT_FW_VERSION_TPM);
+ } else if (!strcasecmp(name,"tpm_kernver")) {
+ value = GetVdatInt(VDAT_INT_KERNEL_VERSION_TPM);
+ } else if (!strcasecmp(name,"tried_fwb")) {
+ value = GetVdatInt(VDAT_INT_TRIED_FIRMWARE_B);
}
return value;
}
-
/* Read a system property string into a destination buffer of the specified
* size.
*
@@ -601,7 +919,7 @@ const char* VbGetSystemPropertyString(const char* name, char* dest, int size) {
return NULL;
}
} else if (!strcasecmp(name,"kernkey_vfy")) {
- switch(VbGetNvStorage(VBNV_FW_VERIFIED_KERNEL_KEY)) {
+ switch(GetVdatInt(VDAT_INT_KERNEL_KEY_VERIFIED)) {
case 0:
return "hash";
case 1:
@@ -609,6 +927,12 @@ const char* VbGetSystemPropertyString(const char* name, char* dest, int size) {
default:
return NULL;
}
+ } else if (!strcasecmp(name, "vdat_timers")) {
+ return GetVdatString(dest, size, VDAT_STRING_TIMERS);
+ } else if (!strcasecmp(name, "vdat_lfdebug")) {
+ return GetVdatString(dest, size, VDAT_STRING_LOAD_FIRMWARE_DEBUG);
+ } else if (!strcasecmp(name, "vdat_lkdebug")) {
+ return GetVdatString(dest, size, VDAT_STRING_LOAD_KERNEL_DEBUG);
} else
return NULL;
}
@@ -625,6 +949,10 @@ int VbSetSystemPropertyInt(const char* name, int value) {
return VbSetNvStorage(VBNV_KERNEL_SETTINGS_RESET, 0);
} else if (!strcasecmp(name,"kern_nv")) {
return VbSetNvStorage(VBNV_KERNEL_FIELD, value);
+ } else if (!strcasecmp(name,"vbtest_errfunc")) {
+ return VbSetNvStorage(VBNV_TEST_ERROR_FUNC, value);
+ } else if (!strcasecmp(name,"vbtest_errno")) {
+ return VbSetNvStorage(VBNV_TEST_ERROR_NUM, value);
}
/* NV storage values. If unable to get from NV storage, fall back to the
* CMOS reboot field used by older BIOS. */
diff --git a/tests/tpm_lite/timing.c b/tests/tpm_lite/timing.c
index 9300d0cc..677b533b 100644
--- a/tests/tpm_lite/timing.c
+++ b/tests/tpm_lite/timing.c
@@ -21,7 +21,7 @@
/* Runs [op] and ensures it returns success and doesn't run longer than
* [time_limit] in milliseconds.
*/
-#define TTPM_CHECK(op, time_limit) do { \
+#define TTPM_CHECK(op, time_limit) do { \
struct timeval before, after; \
int time; \
uint32_t __result; \
@@ -29,7 +29,7 @@
__result = op; \
if (__result != TPM_SUCCESS) { \
printf(#op ": error 0x%x\n", __result); \
- exit(1); \
+ errors++; \
} \
gettimeofday(&after, NULL); \
time = (int) ((after.tv_sec - before.tv_sec) * 1000 + \
@@ -37,15 +37,18 @@
printf(#op ": %d ms\n", time); \
if (time > time_limit) { \
printf(#op " exceeded " #time_limit " ms\n"); \
- exit(1); \
+ time_limit_exceeded = 1; \
} \
} while (0)
int main(int argc, char** argv) {
uint32_t x;
uint8_t in[20], out[20];
+ int time_limit_exceeded = 0;
+ int errors = 0;
TlclLibInit();
+ TTPM_CHECK(0, 50);
TTPM_CHECK(TlclStartupIfNeeded(), 50);
TTPM_CHECK(TlclContinueSelfTest(), 100);
TTPM_CHECK(TlclSelfTestFull(), 1000);
@@ -55,6 +58,11 @@ int main(int argc, char** argv) {
TTPM_CHECK(TlclExtend(0, in, out), 200);
TTPM_CHECK(TlclSetGlobalLock(), 50);
TTPM_CHECK(TlclLockPhysicalPresence(), 100);
- printf("TEST SUCCEEDED\n");
- return 0;
+ if (time_limit_exceeded || errors > 0) {
+ printf("TEST FAILED\n");
+ exit(1);
+ } else {
+ printf("TEST SUCCEEDED\n");
+ return 0;
+ }
}
diff --git a/tests/vboot_nvstorage_test.c b/tests/vboot_nvstorage_test.c
index 40fa6890..5306a648 100644
--- a/tests/vboot_nvstorage_test.c
+++ b/tests/vboot_nvstorage_test.c
@@ -29,8 +29,8 @@ static VbNvField nvfields[] = {
{VBNV_RECOVERY_REQUEST, 0, 0x42, 0xED, "recovery request"},
{VBNV_LOCALIZATION_INDEX, 0, 0x69, 0xB0, "localization index"},
{VBNV_KERNEL_FIELD, 0, 0x12345678, 0xFEDCBA98, "kernel field"},
- {VBNV_TRIED_FIRMWARE_B, 0, 1, 0, "tried firmware B"},
- {VBNV_FW_VERIFIED_KERNEL_KEY, 0, 1, 0, "firmware verified kernel key"},
+ {VBNV_TEST_ERROR_FUNC, 0, 1, 7, "verified boot test error func"},
+ {VBNV_TEST_ERROR_NUM, 0, 3, 6, "verified boot test error number"},
{0, 0, 0, 0, NULL}
};
diff --git a/utility/crossystem_main.c b/utility/crossystem_main.c
index 90ad718b..53166e48 100644
--- a/utility/crossystem_main.c
+++ b/utility/crossystem_main.c
@@ -11,10 +11,18 @@
#include "crossystem.h"
+/* Max length of a string parameter */
+#define MAX_STRING 8192
+
+/* Flags for Param */
+#define IS_STRING 0x01 /* String (not present = integer) */
+#define CAN_WRITE 0x02 /* Writable (not present = read-only */
+#define NO_PRINT_ALL 0x04 /* Don't print contents of parameter when
+ * doing a print-all */
+
typedef struct Param {
const char* name; /* Parameter name */
- int is_string; /* 0 if integer, 1 if string */
- int can_write; /* 0 if read-only, 1 if writable */
+ int flags; /* Flags (see above) */
const char* desc; /* Human-readable description */
const char* format; /* Format string, if non-NULL and 0==is_string*/
} Param;
@@ -22,40 +30,46 @@ typedef struct Param {
/* List of parameters, terminated with a param with NULL name */
const Param sys_param_list[] = {
/* Read-only integers */
- {"devsw_cur", 0, 0, "Developer switch current position"},
- {"devsw_boot", 0, 0, "Developer switch position at boot"},
- {"recoverysw_cur", 0, 0, "Recovery switch current position"},
- {"recoverysw_boot", 0, 0, "Recovery switch position at boot"},
- {"recoverysw_ec_boot", 0, 0, "Recovery switch position at EC boot"},
- {"wpsw_cur", 0, 0, "Firmware write protect switch current position"},
- {"wpsw_boot", 0, 0, "Firmware write protect switch position at boot"},
- {"recovery_reason", 0, 0, "Recovery mode reason for current boot"},
- {"savedmem_base", 0, 0, "RAM debug data area physical address", "0x%08x"},
- {"savedmem_size", 0, 0, "RAM debug data area size in bytes"},
- {"fmap_base", 0, 0, "Main firmware flashmap physical address", "0x%08x"},
- {"tried_fwb", 0, 0, "Tried firmware B before A this boot"},
- {"cros_debug", 0, 0, "OS should allow debug features"},
+ {"devsw_cur", 0, "Developer switch current position"},
+ {"devsw_boot", 0, "Developer switch position at boot"},
+ {"recoverysw_cur", 0, "Recovery switch current position"},
+ {"recoverysw_boot", 0, "Recovery switch position at boot"},
+ {"recoverysw_ec_boot", 0, "Recovery switch position at EC boot"},
+ {"wpsw_cur", 0, "Firmware write protect hardware switch current position"},
+ {"wpsw_boot", 0, "Firmware write protect hardware switch position at boot"},
+ {"recovery_reason", 0, "Recovery mode reason for current boot"},
+ {"savedmem_base", 0, "RAM debug data area physical address", "0x%08x"},
+ {"savedmem_size", 0, "RAM debug data area size in bytes"},
+ {"fmap_base", 0, "Main firmware flashmap physical address", "0x%08x"},
+ {"tried_fwb", 0, "Tried firmware B before A this boot"},
+ {"cros_debug", 0, "OS should allow debug features"},
+ {"vdat_flags", 0, "Flags from VbSharedData", "0x%08x"},
+ {"tpm_fwver", 0, "Firmware version stored in TPM", "0x%08x"},
+ {"tpm_kernver", 0, "Kernel version stored in TPM", "0x%08x"},
/* Read-only strings */
- {"hwid", 1, 0, "Hardware ID"},
- {"fwid", 1, 0, "Active firmware ID"},
- {"ro_fwid", 1, 0, "Read-only firmware ID"},
- {"mainfw_act", 1, 0, "Active main firmware"},
- {"mainfw_type", 1, 0, "Active main firmware type"},
- {"ecfw_act", 1, 0, "Active EC firmware"},
- {"kernkey_vfy", 1, 0, "Type of verification done on kernel key block"},
+ {"hwid", IS_STRING, "Hardware ID"},
+ {"fwid", IS_STRING, "Active firmware ID"},
+ {"ro_fwid", IS_STRING, "Read-only firmware ID"},
+ {"mainfw_act", IS_STRING, "Active main firmware"},
+ {"mainfw_type", IS_STRING, "Active main firmware type"},
+ {"ecfw_act", IS_STRING, "Active EC firmware"},
+ {"kernkey_vfy", IS_STRING, "Type of verification done on kernel key block"},
+ {"vdat_timers", IS_STRING, "Timer values from VbSharedData"},
/* Writable integers */
- {"nvram_cleared", 0, 1, "Have NV settings been lost? Write 0 to clear"},
- {"kern_nv", 0, 1, "Non-volatile field for kernel use", "0x%08x"},
- {"recovery_request", 0, 1, "Recovery mode request (writable)"},
- {"dbg_reset", 0, 1, "Debug reset mode request (writable)"},
- {"fwb_tries", 0, 1, "Try firmware B count (writable)"},
-
- /* TODO: implement the following:
- * nvram_cleared
- */
-
+ {"nvram_cleared", CAN_WRITE, "Have NV settings been lost? Write 0 to clear"},
+ {"kern_nv", CAN_WRITE, "Non-volatile field for kernel use", "0x%08x"},
+ {"recovery_request", CAN_WRITE, "Recovery mode request (writable)"},
+ {"dbg_reset", CAN_WRITE, "Debug reset mode request (writable)"},
+ {"fwb_tries", CAN_WRITE, "Try firmware B count (writable)"},
+ {"vbtest_errfunc", CAN_WRITE, "Verified boot test error function (writable)"},
+ {"vbtest_errno", CAN_WRITE, "Verified boot test error number (writable)"},
+ /* Fields not shown in a print-all list */
+ {"vdat_lfdebug", IS_STRING|NO_PRINT_ALL,
+ "LoadFirmware() debug data (not in print-all)"},
+ {"vdat_lkdebug", IS_STRING|NO_PRINT_ALL,
+ "LoadKernel() debug data (not in print-all)"},
/* Terminate with null name */
- {NULL, 0, 0, NULL}
+ {NULL, 0, NULL}
};
@@ -97,10 +111,10 @@ const Param* FindParam(const char* name) {
*
* Returns 0 if success, non-zero if error. */
int SetParam(const Param* p, const char* value) {
- if (!p->can_write)
+ if (!(p->flags & CAN_WRITE))
return 1; /* Parameter is read-only */
- if (p->is_string) {
+ if (p->flags & IS_STRING) {
return (0 == VbSetSystemPropertyString(p->name, value) ? 0 : 1);
} else {
char* e;
@@ -116,8 +130,8 @@ int SetParam(const Param* p, const char* value) {
*
* Returns 0 if success (match), non-zero if error (mismatch). */
int CheckParam(const Param* p, char* expect) {
- if (p->is_string) {
- char buf[256];
+ if (p->flags & IS_STRING) {
+ char buf[MAX_STRING];
const char* v = VbGetSystemPropertyString(p->name, buf, sizeof(buf));
if (!v || 0 != strcmp(v, expect))
return 1;
@@ -138,8 +152,8 @@ int CheckParam(const Param* p, char* expect) {
*
* Returns 0 if success, non-zero if error. */
int PrintParam(const Param* p) {
- if (p->is_string) {
- char buf[256];
+ if (p->flags & IS_STRING) {
+ char buf[MAX_STRING];
const char* v = VbGetSystemPropertyString(p->name, buf, sizeof(buf));
if (!v)
return 1;
@@ -160,11 +174,13 @@ int PrintParam(const Param* p) {
int PrintAllParams(void) {
const Param* p;
int retval = 0;
- char buf[256];
+ char buf[MAX_STRING];
const char* value;
for (p = sys_param_list; p->name; p++) {
- if (p->is_string) {
+ if (p->flags & NO_PRINT_ALL)
+ continue;
+ if (p->flags & IS_STRING) {
value = VbGetSystemPropertyString(p->name, buf, sizeof(buf));
} else {
int v = VbGetSystemPropertyInt(p->name);
@@ -175,7 +191,7 @@ int PrintAllParams(void) {
value = buf;
}
}
- printf("%-22s = %-20s # %s\n",
+ printf("%-22s = %-30s # %s\n",
p->name, (value ? value : "(error)"), p->desc);
}
return retval;
diff --git a/utility/load_kernel_test.c b/utility/load_kernel_test.c
index d26c7cd4..73a3626a 100644
--- a/utility/load_kernel_test.c
+++ b/utility/load_kernel_test.c
@@ -171,8 +171,8 @@ int main(int argc, char* argv[]) {
}
/* Initialize the shared data area */
- lkp.shared_data_blob = Malloc(LOAD_FIRMWARE_SHARED_DATA_REC_SIZE);
- lkp.shared_data_size = LOAD_FIRMWARE_SHARED_DATA_REC_SIZE;
+ lkp.shared_data_blob = Malloc(VB_SHARED_DATA_REC_SIZE);
+ lkp.shared_data_size = VB_SHARED_DATA_REC_SIZE;
shared = (VbSharedDataHeader*)lkp.shared_data_blob;
if (0 != VbSharedDataInit(shared, lkp.shared_data_size)) {
fprintf(stderr, "Unable to init shared data\n");
diff --git a/utility/tlcl_generator.c b/utility/tlcl_generator.c
index efb0f341..86b7e4ed 100644
--- a/utility/tlcl_generator.c
+++ b/utility/tlcl_generator.c
@@ -222,6 +222,13 @@ Command* BuildStartupCommand(void) {
return cmd;
}
+Command* BuildSaveStateCommand(void) {
+ int size = kTpmRequestHeaderLength;
+ Command* cmd = newCommand(TPM_ORD_SaveState, size);
+ cmd->name = "tpm_savestate_cmd";
+ return cmd;
+}
+
Command* BuildResumeCommand(void) {
int size = kTpmRequestHeaderLength + sizeof(TPM_STARTUP_TYPE);
Command* cmd = newCommand(TPM_ORD_Startup, size);
@@ -452,6 +459,7 @@ Command* (*builders[])(void) = {
BuildPPLockCommand,
BuildFinalizePPCommand,
BuildStartupCommand,
+ BuildSaveStateCommand,
BuildResumeCommand,
BuildSelftestfullCommand,
BuildContinueSelfTestCommand,
diff --git a/utility/tpmc.c b/utility/tpmc.c
index 494a1ee4..fba3ff52 100644
--- a/utility/tpmc.c
+++ b/utility/tpmc.c
@@ -290,6 +290,7 @@ command_record command_table[] = {
{ "getstclearflags", "getvf", "print all volatile (ST_CLEAR) flags",
HandlerGetSTClearFlags },
{ "resume", "res", "execute TPM_Startup(ST_STATE)", TlclResume },
+ { "savestate", "save", "execute TPM_SaveState", TlclSaveState },
};
static int n_commands = sizeof(command_table) / sizeof(command_table[0]);