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 |