blob: 2fbcaa847c68ad16a1f8078be64c7e79bf26840e [file] [log] [blame]
Randall Spanglerd1836442010-06-10 09:59:04 -07001/* Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
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 "vboot_kernel.h"
10
11#include "boot_device.h"
12#include "cgptlib.h"
Bill Richardson5deb67f2010-07-23 17:22:25 -070013#include "cgptlib_internal.h"
Randall Spanglerd1836442010-06-10 09:59:04 -070014#include "load_kernel_fw.h"
15#include "rollback_index.h"
16#include "utility.h"
Randall Spangler83c88cf2010-06-11 16:14:18 -070017#include "vboot_common.h"
18
Randall Spanglerd1836442010-06-10 09:59:04 -070019#define KBUF_SIZE 65536 /* Bytes to read at start of kernel partition */
20
Randall Spangler640fb512011-03-03 10:11:17 -080021typedef enum BootMode {
22 kBootNormal, /* Normal firmware */
23 kBootDev, /* Dev firmware AND dev switch is on */
24 kBootRecovery /* Recovery firmware, regardless of dev switch position */
25} BootMode;
26
Randall Spangler83c88cf2010-06-11 16:14:18 -070027
28/* Allocates and reads GPT data from the drive. The sector_bytes and
29 * drive_sectors fields should be filled on input. The primary and
30 * secondary header and entries are filled on output.
31 *
32 * Returns 0 if successful, 1 if error. */
33int AllocAndReadGptData(GptData* gptdata) {
34
35 uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes;
36
37 /* No data to be written yet */
38 gptdata->modified = 0;
39
40 /* Allocate all buffers */
41 gptdata->primary_header = (uint8_t*)Malloc(gptdata->sector_bytes);
42 gptdata->secondary_header = (uint8_t*)Malloc(gptdata->sector_bytes);
43 gptdata->primary_entries = (uint8_t*)Malloc(TOTAL_ENTRIES_SIZE);
44 gptdata->secondary_entries = (uint8_t*)Malloc(TOTAL_ENTRIES_SIZE);
45
46 if (gptdata->primary_header == NULL || gptdata->secondary_header == NULL ||
47 gptdata->primary_entries == NULL || gptdata->secondary_entries == NULL)
48 return 1;
49
50 /* Read data from the drive, skipping the protective MBR */
51 if (0 != BootDeviceReadLBA(1, 1, gptdata->primary_header))
52 return 1;
53 if (0 != BootDeviceReadLBA(2, entries_sectors, gptdata->primary_entries))
54 return 1;
55 if (0 != BootDeviceReadLBA(gptdata->drive_sectors - entries_sectors - 1,
56 entries_sectors, gptdata->secondary_entries))
57 return 1;
58 if (0 != BootDeviceReadLBA(gptdata->drive_sectors - 1,
59 1, gptdata->secondary_header))
60 return 1;
61
62 return 0;
63}
64
65
66/* Writes any changes for the GPT data back to the drive, then frees
67 * the buffers.
68 *
69 * Returns 0 if successful, 1 if error. */
70int WriteAndFreeGptData(GptData* gptdata) {
71
72 uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes;
73
74 if (gptdata->primary_header) {
75 if (gptdata->modified & GPT_MODIFIED_HEADER1) {
Randall Spanglere2ec9842010-06-23 21:17:07 -070076 VBDEBUG(("Updating GPT header 1\n"));
Randall Spangler83c88cf2010-06-11 16:14:18 -070077 if (0 != BootDeviceWriteLBA(1, 1, gptdata->primary_header))
78 return 1;
79 }
80 Free(gptdata->primary_header);
81 }
82
83 if (gptdata->primary_entries) {
84 if (gptdata->modified & GPT_MODIFIED_ENTRIES1) {
Randall Spanglere2ec9842010-06-23 21:17:07 -070085 VBDEBUG(("Updating GPT entries 1\n"));
Randall Spangler83c88cf2010-06-11 16:14:18 -070086 if (0 != BootDeviceWriteLBA(2, entries_sectors,
87 gptdata->primary_entries))
88 return 1;
89 }
90 Free(gptdata->primary_entries);
91 }
92
93 if (gptdata->secondary_entries) {
94 if (gptdata->modified & GPT_MODIFIED_ENTRIES2) {
Randall Spanglere2ec9842010-06-23 21:17:07 -070095 VBDEBUG(("Updating GPT header 2\n"));
Randall Spangler83c88cf2010-06-11 16:14:18 -070096 if (0 != BootDeviceWriteLBA(gptdata->drive_sectors - entries_sectors - 1,
97 entries_sectors, gptdata->secondary_entries))
98 return 1;
99 }
100 Free(gptdata->secondary_entries);
101 }
102
103 if (gptdata->secondary_header) {
104 if (gptdata->modified & GPT_MODIFIED_HEADER2) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700105 VBDEBUG(("Updating GPT entries 2\n"));
Randall Spangler83c88cf2010-06-11 16:14:18 -0700106 if (0 != BootDeviceWriteLBA(gptdata->drive_sectors - 1, 1,
107 gptdata->secondary_header))
108 return 1;
109 }
110 Free(gptdata->secondary_header);
111 }
112
113 /* Success */
114 return 0;
115}
116
vbendeb3ecaf772010-06-24 16:19:53 -0700117/* disable MSVC warning on const logical expression (as in } while(0);) */
118__pragma(warning(disable: 4127))
Randall Spangler83c88cf2010-06-11 16:14:18 -0700119
Randall Spanglerbd529f02010-06-16 12:51:26 -0700120int LoadKernel(LoadKernelParams* params) {
Randall Spangler640fb512011-03-03 10:11:17 -0800121 VbNvContext* vnc = params->nv_context;
Bill Richardsone2729402010-07-22 12:23:47 -0700122 VbPublicKey* kernel_subkey;
Randall Spanglerd1836442010-06-10 09:59:04 -0700123 GptData gpt;
124 uint64_t part_start, part_size;
Bill Richardsone2729402010-07-22 12:23:47 -0700125 uint64_t blba;
126 uint64_t kbuf_sectors;
Randall Spanglerd1836442010-06-10 09:59:04 -0700127 uint8_t* kbuf = NULL;
128 int found_partitions = 0;
129 int good_partition = -1;
Randall Spangler640fb512011-03-03 10:11:17 -0800130 int good_partition_key_block_valid = 0;
Randall Spangler66680282010-08-16 12:33:44 -0700131 uint32_t tpm_version = 0;
132 uint64_t lowest_version = 0xFFFFFFFF;
Randall Spangler640fb512011-03-03 10:11:17 -0800133 int rec_switch, dev_switch;
134 BootMode boot_mode;
Randall Spangler7a786b72010-07-08 13:29:42 -0700135 uint32_t status;
Randall Spanglerd1836442010-06-10 09:59:04 -0700136
Randall Spangler640fb512011-03-03 10:11:17 -0800137 /* TODO: differentiate between finding an invalid kernel (found_partitions>0)
138 * and not finding one at all. Right now we treat them the same, and return
139 * LOAD_KERNEL_INVALID for both. */
140 int retval = LOAD_KERNEL_INVALID;
141 int recovery = VBNV_RECOVERY_RO_UNSPECIFIED;
142
143 /* Setup NV storage */
144 VbNvSetup(vnc);
145
Bill Richardsone2729402010-07-22 12:23:47 -0700146 /* Sanity Checks */
147 if (!params ||
Bill Richardsone2729402010-07-22 12:23:47 -0700148 !params->bytes_per_lba ||
149 !params->ending_lba ||
150 !params->kernel_buffer ||
151 !params->kernel_buffer_size) {
152 VBDEBUG(("LoadKernel() called with invalid params\n"));
Randall Spangler640fb512011-03-03 10:11:17 -0800153 goto LoadKernelExit;
Bill Richardsone2729402010-07-22 12:23:47 -0700154 }
155
156 /* Initialization */
157 kernel_subkey = (VbPublicKey*)params->header_sign_key_blob;
158 blba = params->bytes_per_lba;
159 kbuf_sectors = KBUF_SIZE / blba;
Randall Spangler4bb5e4b2010-08-19 09:05:22 -0700160 if (0 == kbuf_sectors) {
161 VBDEBUG(("LoadKernel() called with sector size > KBUF_SIZE\n"));
Randall Spangler640fb512011-03-03 10:11:17 -0800162 goto LoadKernelExit;
Randall Spangler4bb5e4b2010-08-19 09:05:22 -0700163 }
164
Randall Spangler640fb512011-03-03 10:11:17 -0800165 rec_switch = (BOOT_FLAG_RECOVERY & params->boot_flags ? 1 : 0);
166 dev_switch = (BOOT_FLAG_DEVELOPER & params->boot_flags ? 1 : 0);
167
168 if (rec_switch)
169 boot_mode = kBootRecovery;
170 else if (BOOT_FLAG_DEV_FIRMWARE & params->boot_flags)
171 if (!dev_switch) {
172 /* Dev firmware should be signed such that it never boots with the dev
173 * switch is off; so something is terribly wrong. */
174 VBDEBUG(("LoadKernel() called with dev firmware but dev switch off\n"));
175 recovery = VBNV_RECOVERY_RW_DEV_MISMATCH;
176 goto LoadKernelExit;
177 }
178 boot_mode = kBootDev;
179 else {
180 /* Normal firmware */
181 boot_mode = kBootNormal;
182 dev_switch = 0; /* Always do a fully verified boot */
Randall Spanglera8e0f942011-02-14 11:12:09 -0800183 }
Bill Richardsone2729402010-07-22 12:23:47 -0700184
Randall Spanglerd1836442010-06-10 09:59:04 -0700185 /* Clear output params in case we fail */
186 params->partition_number = 0;
187 params->bootloader_address = 0;
188 params->bootloader_size = 0;
189
Randall Spangler63dffcb2010-08-05 15:13:14 -0700190 /* Let the TPM know if we're in recovery mode */
Randall Spangler640fb512011-03-03 10:11:17 -0800191 if (kBootRecovery == boot_mode) {
192 if (0 != RollbackKernelRecovery(dev_switch)) {
Randall Spangler63dffcb2010-08-05 15:13:14 -0700193 VBDEBUG(("Error setting up TPM for recovery kernel\n"));
194 /* Ignore return code, since we need to boot recovery mode to
195 * fix the TPM. */
Randall Spangler10788382010-06-23 15:35:31 -0700196 }
Randall Spangler640fb512011-03-03 10:11:17 -0800197 } else {
Randall Spanglerd1836442010-06-10 09:59:04 -0700198 /* Read current kernel key index from TPM. Assumes TPM is already
199 * initialized. */
Randall Spangler66680282010-08-16 12:33:44 -0700200 status = RollbackKernelRead(&tpm_version);
Randall Spangler7a786b72010-07-08 13:29:42 -0700201 if (0 != status) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700202 VBDEBUG(("Unable to get kernel versions from TPM\n"));
Randall Spangler640fb512011-03-03 10:11:17 -0800203 if (status == TPM_E_MUST_REBOOT)
204 retval = LOAD_KERNEL_REBOOT;
205 else
206 recovery = VBNV_RECOVERY_RW_TPM_ERROR;
207 goto LoadKernelExit;
Randall Spangler695cd162010-06-15 23:38:23 -0700208 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700209 }
210
211 do {
212 /* Read GPT data */
Randall Spanglerbeb5bae2010-06-21 16:33:26 -0700213 gpt.sector_bytes = (uint32_t)blba;
Randall Spanglerd1836442010-06-10 09:59:04 -0700214 gpt.drive_sectors = params->ending_lba + 1;
Randall Spangler695cd162010-06-15 23:38:23 -0700215 if (0 != AllocAndReadGptData(&gpt)) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700216 VBDEBUG(("Unable to read GPT data\n"));
Randall Spanglerd1836442010-06-10 09:59:04 -0700217 break;
Randall Spangler695cd162010-06-15 23:38:23 -0700218 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700219
220 /* Initialize GPT library */
Randall Spangler695cd162010-06-15 23:38:23 -0700221 if (GPT_SUCCESS != GptInit(&gpt)) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700222 VBDEBUG(("Error parsing GPT\n"));
Randall Spanglerd1836442010-06-10 09:59:04 -0700223 break;
Randall Spangler695cd162010-06-15 23:38:23 -0700224 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700225
Randall Spanglerd1836442010-06-10 09:59:04 -0700226 /* Allocate kernel header buffers */
227 kbuf = (uint8_t*)Malloc(KBUF_SIZE);
228 if (!kbuf)
229 break;
230
231 /* Loop over candidate kernel partitions */
232 while (GPT_SUCCESS == GptNextKernelEntry(&gpt, &part_start, &part_size)) {
233 VbKeyBlockHeader* key_block;
234 VbKernelPreambleHeader* preamble;
Randall Spangler741d2b22010-08-20 16:37:12 -0700235 RSAPublicKey* data_key = NULL;
Randall Spanglerd1836442010-06-10 09:59:04 -0700236 uint64_t key_version;
Randall Spangler66680282010-08-16 12:33:44 -0700237 uint64_t combined_version;
Randall Spanglerd1836442010-06-10 09:59:04 -0700238 uint64_t body_offset;
Randall Spangler4bb5e4b2010-08-19 09:05:22 -0700239 uint64_t body_offset_sectors;
240 uint64_t body_sectors;
Randall Spangler640fb512011-03-03 10:11:17 -0800241 int key_block_valid = 1;
Randall Spanglerd1836442010-06-10 09:59:04 -0700242
Randall Spanglere2ec9842010-06-23 21:17:07 -0700243 VBDEBUG(("Found kernel entry at %" PRIu64 " size %" PRIu64 "\n",
244 part_start, part_size));
Randall Spangler695cd162010-06-15 23:38:23 -0700245
Randall Spanglerd1836442010-06-10 09:59:04 -0700246 /* Found at least one kernel partition. */
247 found_partitions++;
248
Randall Spangler640fb512011-03-03 10:11:17 -0800249 /* Read the first part of the kernel partition. */
Randall Spangler77ae3892010-09-09 17:37:51 -0700250 if (part_size < kbuf_sectors) {
251 VBDEBUG(("Partition too small to hold kernel.\n"));
Randall Spangler741d2b22010-08-20 16:37:12 -0700252 goto bad_kernel;
Randall Spangler77ae3892010-09-09 17:37:51 -0700253 }
254
255 if (0 != BootDeviceReadLBA(part_start, kbuf_sectors, kbuf)) {
256 VBDEBUG(("Unable to read start of partition.\n"));
Randall Spangler741d2b22010-08-20 16:37:12 -0700257 goto bad_kernel;
Randall Spangler77ae3892010-09-09 17:37:51 -0700258 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700259
Randall Spangler640fb512011-03-03 10:11:17 -0800260 /* Verify the key block. */
Randall Spanglerd1836442010-06-10 09:59:04 -0700261 key_block = (VbKeyBlockHeader*)kbuf;
Randall Spangler640fb512011-03-03 10:11:17 -0800262 if (0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey, 0)) {
263 VBDEBUG(("Verifying key block signature failed.\n"));
264 key_block_valid = 0;
Randall Spanglerd1836442010-06-10 09:59:04 -0700265
Randall Spangler640fb512011-03-03 10:11:17 -0800266 /* If we're not in developer mode, this kernel is bad. */
267 if (kBootDev != boot_mode)
Randall Spangler741d2b22010-08-20 16:37:12 -0700268 goto bad_kernel;
Randall Spangler640fb512011-03-03 10:11:17 -0800269
270 /* In developer mode, we can continue if the SHA-512 hash of the key
271 * block is valid. */
272 if (0 != KeyBlockVerify(key_block, KBUF_SIZE, kernel_subkey, 1)) {
273 VBDEBUG(("Verifying key block hash failed.\n"));
Randall Spangler741d2b22010-08-20 16:37:12 -0700274 goto bad_kernel;
Randall Spanglerae029d92010-07-19 18:26:35 -0700275 }
Randall Spangler695cd162010-06-15 23:38:23 -0700276 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700277
Randall Spangler640fb512011-03-03 10:11:17 -0800278 /* Check the key block flags against the current boot mode. */
279 if (!(key_block->key_block_flags &
280 (dev_switch ? KEY_BLOCK_FLAG_DEVELOPER_1 :
281 KEY_BLOCK_FLAG_DEVELOPER_0))) {
282 VBDEBUG(("Key block developer flag mismatch.\n"));
283 key_block_valid = 0;
284 }
285 if (!(key_block->key_block_flags &
286 (rec_switch ? KEY_BLOCK_FLAG_RECOVERY_1 :
287 KEY_BLOCK_FLAG_RECOVERY_0))) {
288 VBDEBUG(("Key block recovery flag mismatch.\n"));
289 key_block_valid = 0;
290 }
291
292 /* Check for rollback of key version except in recovery mode. */
Randall Spanglerd1836442010-06-10 09:59:04 -0700293 key_version = key_block->data_key.key_version;
Randall Spangler640fb512011-03-03 10:11:17 -0800294 if (kBootRecovery != boot_mode) {
295 if (key_version < (tpm_version >> 16)) {
296 VBDEBUG(("Key version too old.\n"));
297 key_block_valid = 0;
298 }
299 }
300
301 /* If we're not in developer mode, require the key block to be valid. */
302 if (kBootDev != boot_mode && !key_block_valid) {
303 VBDEBUG(("Key block is invalid.\n"));
Randall Spangler741d2b22010-08-20 16:37:12 -0700304 goto bad_kernel;
Randall Spangler695cd162010-06-15 23:38:23 -0700305 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700306
Randall Spangler640fb512011-03-03 10:11:17 -0800307 /* Get the key for preamble/data verification from the key block. */
Randall Spanglerd1836442010-06-10 09:59:04 -0700308 data_key = PublicKeyToRSA(&key_block->data_key);
Randall Spangler77ae3892010-09-09 17:37:51 -0700309 if (!data_key) {
310 VBDEBUG(("Data key bad.\n"));
Randall Spangler741d2b22010-08-20 16:37:12 -0700311 goto bad_kernel;
Randall Spangler77ae3892010-09-09 17:37:51 -0700312 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700313
314 /* Verify the preamble, which follows the key block */
315 preamble = (VbKernelPreambleHeader*)(kbuf + key_block->key_block_size);
Randall Spangler87c13d82010-07-19 10:35:40 -0700316 if ((0 != VerifyKernelPreamble(preamble,
Randall Spanglerd1836442010-06-10 09:59:04 -0700317 KBUF_SIZE - key_block->key_block_size,
318 data_key))) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700319 VBDEBUG(("Preamble verification failed.\n"));
Randall Spangler741d2b22010-08-20 16:37:12 -0700320 goto bad_kernel;
Randall Spanglerd1836442010-06-10 09:59:04 -0700321 }
322
Randall Spangler640fb512011-03-03 10:11:17 -0800323 /* If the key block is valid and we're not in recovery mode, check for
324 * rollback of the kernel version. */
Randall Spangler66680282010-08-16 12:33:44 -0700325 combined_version = ((key_version << 16) |
326 (preamble->kernel_version & 0xFFFF));
Randall Spangler640fb512011-03-03 10:11:17 -0800327 if (key_block_valid && kBootRecovery != boot_mode) {
328 if (combined_version < tpm_version) {
329 VBDEBUG(("Kernel version too low.\n"));
330 /* If we're not in developer mode, kernel version must be valid. */
331 if (kBootDev != boot_mode)
332 goto bad_kernel;
333 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700334 }
335
Randall Spanglere2ec9842010-06-23 21:17:07 -0700336 VBDEBUG(("Kernel preamble is good.\n"));
Randall Spangler695cd162010-06-15 23:38:23 -0700337
Randall Spangler66680282010-08-16 12:33:44 -0700338 /* Check for lowest version from a valid header. */
Randall Spangler640fb512011-03-03 10:11:17 -0800339 if (key_block_valid && lowest_version > combined_version)
Randall Spangler66680282010-08-16 12:33:44 -0700340 lowest_version = combined_version;
Randall Spanglerd1836442010-06-10 09:59:04 -0700341
342 /* If we already have a good kernel, no need to read another
343 * one; we only needed to look at the versions to check for
Randall Spangler77ae3892010-09-09 17:37:51 -0700344 * rollback. So skip to the next kernel preamble. */
Randall Spanglerd1836442010-06-10 09:59:04 -0700345 if (-1 != good_partition)
Randall Spangler77ae3892010-09-09 17:37:51 -0700346 continue;
Randall Spanglerd1836442010-06-10 09:59:04 -0700347
348 /* Verify body load address matches what we expect */
Randall Spangler695cd162010-06-15 23:38:23 -0700349 if ((preamble->body_load_address != (size_t)params->kernel_buffer) &&
350 !(params->boot_flags & BOOT_FLAG_SKIP_ADDR_CHECK)) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700351 VBDEBUG(("Wrong body load address.\n"));
Randall Spangler741d2b22010-08-20 16:37:12 -0700352 goto bad_kernel;
Randall Spanglerd1836442010-06-10 09:59:04 -0700353 }
354
355 /* Verify kernel body starts at a multiple of the sector size. */
356 body_offset = key_block->key_block_size + preamble->preamble_size;
357 if (0 != body_offset % blba) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700358 VBDEBUG(("Kernel body not at multiple of sector size.\n"));
Randall Spangler741d2b22010-08-20 16:37:12 -0700359 goto bad_kernel;
Randall Spanglerd1836442010-06-10 09:59:04 -0700360 }
Randall Spangler4bb5e4b2010-08-19 09:05:22 -0700361 body_offset_sectors = body_offset / blba;
362
363 /* Verify kernel body fits in the buffer */
364 body_sectors = (preamble->body_signature.data_size + blba - 1) / blba;
365 if (body_sectors * blba > params->kernel_buffer_size) {
366 VBDEBUG(("Kernel body doesn't fit in memory.\n"));
Randall Spangler741d2b22010-08-20 16:37:12 -0700367 goto bad_kernel;
Randall Spangler4bb5e4b2010-08-19 09:05:22 -0700368 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700369
370 /* Verify kernel body fits in the partition */
Randall Spangler4bb5e4b2010-08-19 09:05:22 -0700371 if (body_offset_sectors + body_sectors > part_size) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700372 VBDEBUG(("Kernel body doesn't fit in partition.\n"));
Randall Spangler741d2b22010-08-20 16:37:12 -0700373 goto bad_kernel;
Randall Spanglerd1836442010-06-10 09:59:04 -0700374 }
375
376 /* Read the kernel data */
Randall Spangler6078ca32010-10-18 15:49:28 -0700377 VBPERFSTART("VB_RKD");
Randall Spangler4bb5e4b2010-08-19 09:05:22 -0700378 if (0 != BootDeviceReadLBA(part_start + body_offset_sectors,
379 body_sectors,
380 params->kernel_buffer)) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700381 VBDEBUG(("Unable to read kernel data.\n"));
Randall Spangler6078ca32010-10-18 15:49:28 -0700382 VBPERFEND("VB_RKD");
Randall Spangler741d2b22010-08-20 16:37:12 -0700383 goto bad_kernel;
Randall Spanglerd1836442010-06-10 09:59:04 -0700384 }
Randall Spangler6078ca32010-10-18 15:49:28 -0700385 VBPERFEND("VB_RKD");
Randall Spanglerd1836442010-06-10 09:59:04 -0700386
387 /* Verify kernel data */
388 if (0 != VerifyData((const uint8_t*)params->kernel_buffer,
Randall Spangler87c13d82010-07-19 10:35:40 -0700389 params->kernel_buffer_size,
Randall Spanglerd1836442010-06-10 09:59:04 -0700390 &preamble->body_signature, data_key)) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700391 VBDEBUG(("Kernel data verification failed.\n"));
Randall Spangler741d2b22010-08-20 16:37:12 -0700392 goto bad_kernel;
Randall Spanglerd1836442010-06-10 09:59:04 -0700393 }
394
395 /* Done with the kernel signing key, so can free it now */
396 RSAPublicKeyFree(data_key);
Randall Spangler741d2b22010-08-20 16:37:12 -0700397 data_key = NULL;
Randall Spanglerd1836442010-06-10 09:59:04 -0700398
399 /* If we're still here, the kernel is valid. */
400 /* Save the first good partition we find; that's the one we'll boot */
Randall Spangler640fb512011-03-03 10:11:17 -0800401 VBDEBUG(("Partition is good.\n"));
402 good_partition_key_block_valid = key_block_valid;
Randall Spanglerbeb5bae2010-06-21 16:33:26 -0700403 /* TODO: GPT partitions start at 1, but cgptlib starts them at 0.
404 * Adjust here, until cgptlib is fixed. */
405 good_partition = gpt.current_kernel + 1;
Randall Spanglerb9d60a52010-06-23 12:43:01 -0700406 params->partition_number = gpt.current_kernel + 1;
Bill Richardson5deb67f2010-07-23 17:22:25 -0700407 GetCurrentKernelUniqueGuid(&gpt, &params->partition_guid);
Randall Spangler63dffcb2010-08-05 15:13:14 -0700408 /* TODO: GetCurrentKernelUniqueGuid() should take a destination size, or
409 * the dest should be a struct, so we know it's big enough. */
Randall Spangler695cd162010-06-15 23:38:23 -0700410 params->bootloader_address = preamble->bootloader_address;
411 params->bootloader_size = preamble->bootloader_size;
Randall Spangler741d2b22010-08-20 16:37:12 -0700412
413 /* Update GPT to note this is the kernel we're trying */
414 GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_TRY);
415
Randall Spangler640fb512011-03-03 10:11:17 -0800416 /* If we're in recovery mode or we're about to boot a dev-signed kernel,
417 * there's no rollback protection, so we can stop at the first valid
418 * kernel. */
419 if (kBootRecovery == boot_mode || !key_block_valid) {
420 VBDEBUG(("In recovery mode or dev-signed kernel\n"));
Randall Spangler695cd162010-06-15 23:38:23 -0700421 break;
Randall Spanglerbeb5bae2010-06-21 16:33:26 -0700422 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700423
Randall Spangler640fb512011-03-03 10:11:17 -0800424 /* Otherwise, we do care about the key index in the TPM. If the good
425 * partition's key version is the same as the tpm, then the TPM doesn't
426 * need updating; we can stop now. Otherwise, we'll check all the other
427 * headers to see if they contain a newer key. */
Randall Spangler66680282010-08-16 12:33:44 -0700428 if (combined_version == tpm_version) {
429 VBDEBUG(("Same kernel version\n"));
Randall Spangler695cd162010-06-15 23:38:23 -0700430 break;
Randall Spanglerbeb5bae2010-06-21 16:33:26 -0700431 }
Randall Spangler741d2b22010-08-20 16:37:12 -0700432
433 /* Continue, so that we skip the error handling code below */
434 continue;
435
436 bad_kernel:
437 /* Handle errors parsing this kernel */
438 if (NULL != data_key)
439 RSAPublicKeyFree(data_key);
440
441 VBDEBUG(("Marking kernel as invalid.\n"));
442 GptUpdateKernelEntry(&gpt, GPT_UPDATE_ENTRY_BAD);
443
444
Randall Spanglerd1836442010-06-10 09:59:04 -0700445 } /* while(GptNextKernelEntry) */
446 } while(0);
447
448 /* Free kernel buffer */
449 if (kbuf)
450 Free(kbuf);
451
452 /* Write and free GPT data */
453 WriteAndFreeGptData(&gpt);
454
455 /* Handle finding a good partition */
456 if (good_partition >= 0) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700457 VBDEBUG(("Good_partition >= 0\n"));
Randall Spanglerd1836442010-06-10 09:59:04 -0700458
459 /* See if we need to update the TPM */
Randall Spangler640fb512011-03-03 10:11:17 -0800460 if (kBootRecovery != boot_mode) {
461 /* We only update the TPM in normal and developer boot modes. In
462 * developer mode, we only advanced lowest_version for kernels with valid
463 * key blocks, and didn't count self-signed key blocks. In recovery
464 * mode, the TPM stays PP-unlocked, so anything we write gets blown away
465 * by the firmware when we go back to normal mode. */
466 VBDEBUG(("Boot_flags = not recovery\n"));
Randall Spangler66680282010-08-16 12:33:44 -0700467 if (lowest_version > tpm_version) {
468 status = RollbackKernelWrite((uint32_t)lowest_version);
Randall Spangler7a786b72010-07-08 13:29:42 -0700469 if (0 != status) {
Randall Spanglere2ec9842010-06-23 21:17:07 -0700470 VBDEBUG(("Error writing kernel versions to TPM.\n"));
Randall Spangler640fb512011-03-03 10:11:17 -0800471 if (status == TPM_E_MUST_REBOOT)
472 retval = LOAD_KERNEL_REBOOT;
473 else
474 recovery = VBNV_RECOVERY_RW_TPM_ERROR;
475 goto LoadKernelExit;
Randall Spangler10788382010-06-23 15:35:31 -0700476 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700477 }
478 }
479
Randall Spangler63dffcb2010-08-05 15:13:14 -0700480 /* Lock the kernel versions */
481 status = RollbackKernelLock();
482 if (0 != status) {
483 VBDEBUG(("Error locking kernel versions.\n"));
484 /* Don't reboot to recovery mode if we're already there */
Randall Spangler640fb512011-03-03 10:11:17 -0800485 if (kBootRecovery != boot_mode) {
486 if (status == TPM_E_MUST_REBOOT)
487 retval = LOAD_KERNEL_REBOOT;
488 else
489 recovery = VBNV_RECOVERY_RW_TPM_ERROR;
490 goto LoadKernelExit;
491 }
Randall Spanglerd1836442010-06-10 09:59:04 -0700492 }
493
494 /* Success! */
Randall Spangler640fb512011-03-03 10:11:17 -0800495 retval = LOAD_KERNEL_SUCCESS;
Randall Spanglerd1836442010-06-10 09:59:04 -0700496 }
497
Randall Spangler640fb512011-03-03 10:11:17 -0800498LoadKernelExit:
499
500 /* Save whether the good partition's key block was fully verified */
501 VbNvSet(vnc, VBNV_FW_VERIFIED_KERNEL_KEY, good_partition_key_block_valid);
502
503 /* Store recovery request, if any, then tear down non-volatile storage */
504 VbNvSet(vnc, VBNV_RECOVERY_REQUEST, LOAD_KERNEL_RECOVERY == retval ?
505 recovery : VBNV_RECOVERY_NOT_REQUESTED);
506 VbNvTeardown(vnc);
507
508 return retval;
Randall Spanglerd1836442010-06-10 09:59:04 -0700509}