Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | |
|
gauravsh
2010/06/10 14:44:13
TODO REview
| |
| 2 * Use of this source code is governed by a BSD-style license that can be | |
| 3 * found in the LICENSE file. | |
| 4 * | |
| 5 * Functions for loading a kernel from disk. | |
| 6 * (Firmware portion) | |
| 7 */ | |
| 8 | |
| 9 #include "boot_device.h" | |
| 10 #include "cgptlib.h" | |
| 11 #include "load_kernel_fw.h" | |
| 12 #include "rollback_index.h" | |
| 13 #include "utility.h" | |
| 14 #include "vboot_kernel.h" | |
|
gauravsh
2010/06/10 14:44:13
nit: this include should go first.
| |
| 15 | |
| 16 #define KBUF_SIZE 65536 /* Bytes to read at start of kernel partition */ | |
| 17 | |
| 18 int LoadKernel2(LoadKernelParams* params) { | |
| 19 | |
| 20 VbPublicKey* kernel_subkey = (VbPublicKey*)params->header_sign_key_blob; | |
| 21 | |
| 22 GptData gpt; | |
| 23 uint64_t part_start, part_size; | |
| 24 uint64_t blba = params->bytes_per_lba; | |
| 25 uint64_t kbuf_sectors = KBUF_SIZE / blba; | |
| 26 uint8_t* kbuf = NULL; | |
| 27 int found_partitions = 0; | |
| 28 int good_partition = -1; | |
| 29 uint16_t tpm_key_version = 0; | |
| 30 uint16_t tpm_kernel_version = 0; | |
| 31 uint64_t lowest_key_version = 0xFFFF; | |
| 32 uint64_t lowest_kernel_version = 0xFFFF; | |
| 33 int is_dev = ((BOOT_FLAG_DEVELOPER & params->boot_flags) && | |
| 34 !(BOOT_FLAG_RECOVERY & params->boot_flags)); | |
| 35 int is_normal = (!(BOOT_FLAG_DEVELOPER & params->boot_flags) && | |
| 36 !(BOOT_FLAG_RECOVERY & params->boot_flags)); | |
| 37 | |
| 38 /* Clear output params in case we fail */ | |
| 39 params->partition_number = 0; | |
| 40 params->bootloader_address = 0; | |
| 41 params->bootloader_size = 0; | |
| 42 | |
| 43 if (is_normal) { | |
| 44 /* Read current kernel key index from TPM. Assumes TPM is already | |
| 45 * initialized. */ | |
| 46 if (0 != GetStoredVersions(KERNEL_VERSIONS, | |
| 47 &tpm_key_version, | |
| 48 &tpm_kernel_version)) | |
| 49 return LOAD_KERNEL_RECOVERY; | |
| 50 } else if (is_dev) { | |
| 51 /* In developer mode, we ignore the kernel subkey, and just use | |
| 52 * the SHA-512 hash to verify the key block. */ | |
| 53 kernel_subkey = NULL; | |
| 54 } | |
| 55 | |
| 56 do { | |
| 57 /* Read GPT data */ | |
| 58 gpt.sector_bytes = blba; | |
| 59 gpt.drive_sectors = params->ending_lba + 1; | |
| 60 if (0 != AllocAndReadGptData(&gpt)) | |
| 61 break; | |
| 62 | |
| 63 /* Initialize GPT library */ | |
| 64 if (GPT_SUCCESS != GptInit(&gpt)) | |
| 65 break; | |
| 66 | |
| 67 /* TODO: TERRIBLE KLUDGE - fake partition attributes */ | |
| 68 FakePartitionAttributes(&gpt); | |
| 69 | |
| 70 /* Allocate kernel header buffers */ | |
| 71 kbuf = (uint8_t*)Malloc(KBUF_SIZE); | |
| 72 if (!kbuf) | |
| 73 break; | |
| 74 | |
| 75 /* Loop over candidate kernel partitions */ | |
| 76 while (GPT_SUCCESS == GptNextKernelEntry(&gpt, &part_start, &part_size)) { | |
| 77 VbKeyBlockHeader* key_block; | |
| 78 VbKernelPreambleHeader* preamble; | |
| 79 RSAPublicKey* data_key; | |
| 80 uint64_t key_version; | |
| 81 uint64_t body_offset; | |
| 82 | |
| 83 /* Found at least one kernel partition. */ | |
| 84 found_partitions++; | |
| 85 | |
| 86 /* Read the first part of the kernel partition */ | |
| 87 if (part_size < kbuf_sectors) | |
| 88 continue; | |
| 89 if (0 != BootDeviceReadLBA(part_start, kbuf_sectors, kbuf)) | |
| 90 continue; | |
| 91 | |
| 92 /* Verify the key block */ | |
| 93 key_block = (VbKeyBlockHeader*)kbuf; | |
| 94 if ((0 != VerifyKeyBlock(key_block, KBUF_SIZE, kernel_subkey))) | |
| 95 continue; | |
| 96 | |
| 97 /* Check the key block flags against the current boot mode */ | |
| 98 if (!(key_block->key_block_flags && | |
| 99 ((BOOT_FLAG_DEVELOPER & params->boot_flags) ? | |
| 100 KEY_BLOCK_FLAG_DEVELOPER_1 : KEY_BLOCK_FLAG_DEVELOPER_0))) | |
| 101 continue; | |
| 102 if (!(key_block->key_block_flags && | |
| 103 ((BOOT_FLAG_RECOVERY & params->boot_flags) ? | |
| 104 KEY_BLOCK_FLAG_RECOVERY_1 : KEY_BLOCK_FLAG_RECOVERY_0))) | |
| 105 continue; | |
| 106 | |
| 107 /* Check for rollback of key version. Note this is implicitly | |
| 108 * skipped in recovery and developer modes because those set | |
| 109 * key_version=0 above. */ | |
| 110 key_version = key_block->data_key.key_version; | |
| 111 if (key_version < tpm_key_version) | |
| 112 continue; | |
| 113 | |
| 114 /* Get the key for preamble/data verification from the key block */ | |
| 115 data_key = PublicKeyToRSA(&key_block->data_key); | |
| 116 if (!data_key) | |
| 117 continue; | |
| 118 | |
| 119 /* Verify the preamble, which follows the key block */ | |
| 120 preamble = (VbKernelPreambleHeader*)(kbuf + key_block->key_block_size); | |
| 121 if ((0 != VerifyKernelPreamble2(preamble, | |
| 122 KBUF_SIZE - key_block->key_block_size, | |
| 123 data_key))) { | |
| 124 RSAPublicKeyFree(data_key); | |
| 125 continue; | |
| 126 } | |
| 127 | |
| 128 /* Check for rollback of kernel version. Note this is implicitly | |
| 129 * skipped in recovery and developer modes because those set | |
| 130 * key_version=0 and kernel_version=0 above. */ | |
| 131 if (key_version == tpm_key_version && | |
| 132 preamble->kernel_version < tpm_kernel_version) { | |
| 133 RSAPublicKeyFree(data_key); | |
| 134 continue; | |
| 135 } | |
| 136 | |
| 137 /* Check for lowest key version from a valid header. */ | |
| 138 if (lowest_key_version > key_version) { | |
| 139 lowest_key_version = key_version; | |
| 140 lowest_kernel_version = preamble->kernel_version; | |
| 141 } | |
| 142 else if (lowest_key_version == key_version && | |
| 143 lowest_kernel_version > preamble->kernel_version) { | |
| 144 lowest_kernel_version = preamble->kernel_version; | |
| 145 } | |
| 146 | |
| 147 /* If we already have a good kernel, no need to read another | |
| 148 * one; we only needed to look at the versions to check for | |
| 149 * rollback. */ | |
| 150 if (-1 != good_partition) | |
| 151 continue; | |
| 152 | |
| 153 /* Verify body load address matches what we expect */ | |
| 154 if (preamble->body_load_address != (size_t)params->kernel_buffer) { | |
| 155 RSAPublicKeyFree(data_key); | |
| 156 continue; | |
| 157 } | |
| 158 | |
| 159 /* Verify kernel body starts at a multiple of the sector size. */ | |
| 160 body_offset = key_block->key_block_size + preamble->preamble_size; | |
| 161 if (0 != body_offset % blba) { | |
| 162 RSAPublicKeyFree(data_key); | |
| 163 continue; | |
| 164 } | |
| 165 | |
| 166 /* Verify kernel body fits in the partition */ | |
| 167 if (body_offset + preamble->body_signature.data_size > | |
| 168 part_size * blba) { | |
| 169 RSAPublicKeyFree(data_key); | |
| 170 continue; | |
| 171 } | |
| 172 | |
| 173 /* Read the kernel data */ | |
| 174 if (0 != BootDeviceReadLBA( | |
| 175 part_start + (body_offset / blba), | |
| 176 (preamble->body_signature.data_size + blba - 1) / blba, | |
| 177 params->kernel_buffer)) { | |
| 178 RSAPublicKeyFree(data_key); | |
| 179 continue; | |
| 180 } | |
| 181 | |
| 182 /* Verify kernel data */ | |
| 183 if (0 != VerifyData((const uint8_t*)params->kernel_buffer, | |
| 184 &preamble->body_signature, data_key)) { | |
| 185 RSAPublicKeyFree(data_key); | |
| 186 continue; | |
| 187 } | |
| 188 | |
| 189 /* Done with the kernel signing key, so can free it now */ | |
| 190 RSAPublicKeyFree(data_key); | |
| 191 | |
| 192 /* If we're still here, the kernel is valid. */ | |
| 193 /* Save the first good partition we find; that's the one we'll boot */ | |
| 194 if (-1 == good_partition) { | |
| 195 good_partition = gpt.current_kernel; | |
| 196 params->partition_number = gpt.current_kernel; | |
| 197 params->bootloader_address = preamble->bootloader_address; | |
| 198 params->bootloader_size = preamble->bootloader_size; | |
| 199 /* If we're in developer or recovery mode, there's no rollback | |
| 200 * protection, so we can stop at the first valid kernel. */ | |
| 201 if (!is_normal) | |
| 202 break; | |
| 203 | |
| 204 /* Otherwise, we're in normal boot mode, so we do care about | |
| 205 * the key index in the TPM. If the good partition's key | |
| 206 * version is the same as the tpm, then the TPM doesn't need | |
| 207 * updating; we can stop now. Otherwise, we'll check all the | |
| 208 * other headers to see if they contain a newer key. */ | |
| 209 if (key_version == tpm_key_version && | |
| 210 preamble->kernel_version == tpm_kernel_version) | |
| 211 break; | |
| 212 } | |
| 213 } /* while(GptNextKernelEntry) */ | |
| 214 } while(0); | |
| 215 | |
| 216 /* Free kernel buffer */ | |
| 217 if (kbuf) | |
| 218 Free(kbuf); | |
| 219 | |
| 220 /* Write and free GPT data */ | |
| 221 WriteAndFreeGptData(&gpt); | |
| 222 | |
| 223 /* Handle finding a good partition */ | |
| 224 if (good_partition >= 0) { | |
| 225 | |
| 226 /* See if we need to update the TPM */ | |
| 227 if (is_normal) { | |
| 228 /* We only update the TPM in normal boot mode. In developer | |
| 229 * mode, the kernel is self-signed by the developer, so we can't | |
| 230 * trust the key version and wouldn't want to roll the TPM | |
| 231 * forward. In recovery mode, the TPM stays PP-unlocked, so | |
| 232 * anything we write gets blown away by the firmware when we go | |
| 233 * back to normal mode. */ | |
| 234 if ((lowest_key_version > tpm_key_version) || | |
| 235 (lowest_key_version == tpm_key_version && | |
| 236 lowest_kernel_version > tpm_kernel_version)) { | |
| 237 if (0 != WriteStoredVersions(KERNEL_VERSIONS, | |
| 238 lowest_key_version, | |
| 239 lowest_kernel_version)) | |
| 240 return LOAD_KERNEL_RECOVERY; | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 if (!(BOOT_FLAG_RECOVERY & params->boot_flags)) { | |
| 245 /* We can lock the TPM now, since we've decided which kernel we | |
| 246 * like. If we don't find a good kernel, we leave the TPM | |
| 247 * unlocked so we can try again on the next boot device. If no | |
| 248 * kernels are good, we'll reboot to recovery mode, so it's ok to | |
| 249 * leave the TPM unlocked in that case too. | |
| 250 * | |
| 251 * If we're already in recovery mode, we need to leave PP unlocked, | |
| 252 * so don't lock the kernel versions. */ | |
| 253 if (0 != LockKernelVersionsByLockingPP()) | |
| 254 return LOAD_KERNEL_RECOVERY; | |
| 255 } | |
| 256 | |
| 257 /* Success! */ | |
| 258 return LOAD_KERNEL_SUCCESS; | |
| 259 } | |
| 260 | |
| 261 // Handle error cases | |
| 262 if (found_partitions) | |
| 263 return LOAD_KERNEL_INVALID; | |
| 264 else | |
| 265 return LOAD_KERNEL_NOT_FOUND; | |
| 266 } | |
| OLD | NEW |