diff options
author | Randall Spangler <rspangler@chromium.org> | 2013-01-29 15:01:12 -0800 |
---|---|---|
committer | ChromeBot <chrome-bot@google.com> | 2013-01-31 09:38:28 -0800 |
commit | 7993f257af87c7c38cdc71b76bc67cde6c3cdbca (patch) | |
tree | 6f0153579ebf4d0ae327e7f11dda902c6494b4ce /firmware/lib | |
parent | 49cb0d3471e768da11fe76b65769bd57dca38bd7 (diff) | |
download | vboot-7993f257af87c7c38cdc71b76bc67cde6c3cdbca.tar.gz |
Reformat to kernel style
No code changes, just reformatting.
BUG=none
BRANCH=none
TEST=make runtests
Change-Id: Id690c8334147970784db5ac54933ad1f5a58dcc1
Reviewed-on: https://gerrit.chromium.org/gerrit/42263
Tested-by: Randall Spangler <rspangler@chromium.org>
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Commit-Queue: Randall Spangler <rspangler@chromium.org>
Diffstat (limited to 'firmware/lib')
-rw-r--r-- | firmware/lib/crc8.c | 38 | ||||
-rw-r--r-- | firmware/lib/rollback_index.c | 21 | ||||
-rw-r--r-- | firmware/lib/stateful_util.c | 106 | ||||
-rw-r--r-- | firmware/lib/tpm_bootmode.c | 187 | ||||
-rw-r--r-- | firmware/lib/utility.c | 26 | ||||
-rw-r--r-- | firmware/lib/utility_string.c | 108 | ||||
-rw-r--r-- | firmware/lib/vboot_api_firmware.c | 204 | ||||
-rw-r--r-- | firmware/lib/vboot_api_init.c | 551 | ||||
-rw-r--r-- | firmware/lib/vboot_audio.c | 463 | ||||
-rw-r--r-- | firmware/lib/vboot_firmware.c | 643 | ||||
-rw-r--r-- | firmware/lib/vboot_kernel.c | 1126 | ||||
-rw-r--r-- | firmware/lib/vboot_nvstorage.c | 455 |
12 files changed, 2080 insertions, 1848 deletions
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/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/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_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_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 a9a6fdf3..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,551 +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->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; - } +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) */ - - 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; + /* 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; } |