Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: src/platform/vboot_reference/vboot_firmware/lib/load_kernel_fw.c

Issue 2327002: Initial LoadKernel() implementation. (Closed) Base URL: ssh://gitrw.chromium.org/chromiumos
Patch Set: Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 /* 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 "load_kernel_fw.h"
10
11 #include "boot_device.h"
12 #include "cgptlib.h"
13 #include "kernel_image_fw.h"
14 #include "rollback_index.h"
15 #include "utility.h"
16
17
18 int AllocAndReadGptData(GptData *gptdata) {
19 /* Allocates and reads GPT data from the drive. The sector_bytes and
20 * drive_sectors fields should be filled on input. The primary and
21 * secondary header and entries are filled on output.
22 *
23 * Returns 0 if successful, 1 if error. */
24
25 uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes;
26
27 /* No data to be written yet */
28 gptdata->modified = 0;
29
30 /* Allocate all buffers */
31 gptdata->primary_header = (uint8_t*)Malloc(gptdata->sector_bytes);
32 gptdata->secondary_header = (uint8_t*)Malloc(gptdata->sector_bytes);
33 gptdata->primary_entries = (uint8_t*)Malloc(TOTAL_ENTRIES_SIZE);
34 gptdata->secondary_entries = (uint8_t*)Malloc(TOTAL_ENTRIES_SIZE);
35
36 if (gptdata->primary_header == NULL || gptdata->secondary_header == NULL ||
37 gptdata->primary_entries == NULL || gptdata->secondary_entries == NULL)
38 return 1;
39
40 /* Read data from the drive */
41 if (0 != BootDeviceReadLBA(0, 1, gptdata->primary_header))
42 return 1;
43 if (0 != BootDeviceReadLBA(1, entries_sectors, gptdata->primary_entries))
44 return 1;
45 if (0 != BootDeviceReadLBA(gptdata->drive_sectors - entries_sectors - 1,
46 entries_sectors, gptdata->secondary_entries))
47 return 1;
48 if (0 != BootDeviceReadLBA(gptdata->drive_sectors - entries_sectors - 1,
49 1, gptdata->secondary_header))
50 return 1;
51
52 return 0;
53 }
54
55 void WriteAndFreeGptData(GptData *gptdata) {
56 /* Writes any changes for the GPT data back to the drive, then frees the
57 * buffers. */
58
59 uint64_t entries_sectors = TOTAL_ENTRIES_SIZE / gptdata->sector_bytes;
60
61 if (gptdata->primary_header) {
62 if (gptdata->modified & GPT_MODIFIED_HEADER1)
63 BootDeviceWriteLBA(0, 1, gptdata->primary_header);
64 Free(gptdata->primary_header);
65 }
66
67 if (gptdata->primary_entries) {
68 if (gptdata->modified & GPT_MODIFIED_ENTRIES1)
69 BootDeviceWriteLBA(1, entries_sectors, gptdata->primary_entries);
70 Free(gptdata->primary_entries);
71 }
72
73 if (gptdata->secondary_entries) {
74 if (gptdata->modified & GPT_MODIFIED_ENTRIES2)
75 BootDeviceWriteLBA(gptdata->drive_sectors - entries_sectors - 1,
76 entries_sectors, gptdata->secondary_entries);
77 Free(gptdata->secondary_entries);
78 }
79
80 if (gptdata->secondary_header) {
81 if (gptdata->modified & GPT_MODIFIED_HEADER2)
82 BootDeviceWriteLBA(gptdata->drive_sectors - entries_sectors - 1,
83 1, gptdata->secondary_header);
84 BootDeviceWriteLBA(0, 1, gptdata->primary_header);
85 Free(gptdata->primary_header);
86 }
87 /* TODO: What to do with return codes from the writes? */
88 }
89
90 #define KBUF_SIZE 65536 /* Bytes to read at start of kernel partition */
91
92 int LoadKernel(LoadKernelParams* params) {
93
94 GptData gpt;
95 uint64_t part_start, part_size;
96 uint64_t blba = params->bytes_per_lba;
97 uint8_t* kbuf = NULL;
98 uint64_t kbuf_sectors;
99 int found_partition = 0;
100 int good_partition = -1;
101 uint16_t tpm_kernel_key_version, tpm_kernel_version;
102 uint16_t lowest_kernel_key_version = 0xFFFF;
103 uint16_t lowest_kernel_version = 0xFFFF;
104 KernelImage *kim = NULL;
105
106 /* Read current kernel key index from TPM. Assumes TPM is already
107 * initialized. */
108 /* TODO: Is that a safe assumption? Normally, SetupTPM() would be called
109 * when the RW firmware is verified. Is it harmful to call SetupTPM()
110 * again if it's already initialized? It'd be easier if we could just do
111 * that. */
112 tpm_kernel_key_version = GetStoredVersion(KERNEL_KEY_VERSION);
113 tpm_kernel_version = GetStoredVersion(KERNEL_VERSION);
114
115 do {
116 /* Read GPT data */
117 gpt.sector_bytes = blba;
118 gpt.drive_sectors = params->ending_lba + 1;
119 if (0 != AllocAndReadGptData(&gpt))
120 break;
121
122 /* Initialize GPT library */
123 if (GPT_SUCCESS != GptInit(&gpt))
124 break;
125
126 /* Allocate kernel header and image work buffers */
127 kbuf = (uint8_t*)Malloc(KBUF_SIZE);
128 if (!kbuf)
129 break;
130 kbuf_sectors = KBUF_SIZE / blba;
131 kim = (KernelImage*)Malloc(sizeof(KernelImage));
132 if (!kim)
133 break;
134
135 /* Loop over candidate kernel partitions */
136 while (GPT_SUCCESS == GptNextKernelEntry(&gpt, &part_start, &part_size)) {
137 RSAPublicKey *kernel_sign_key = NULL;
138 int kernel_start, kernel_sectors;
139
140 /* Found at least one kernel partition. */
141 found_partition = 1;
142
143 /* Read the first part of the kernel partition */
144 if (part_size < kbuf_sectors)
145 continue;
146 if (1 != BootDeviceReadLBA(part_start, kbuf_sectors, kbuf))
147 continue;
148
149 /* Verify the kernel header and preamble */
150 if (VERIFY_KERNEL_SUCCESS != VerifyKernelHeader(
151 params->header_sign_key_blob,
152 kbuf,
153 KBUF_SIZE,
154 (BOOT_MODE_DEVELOPER == params->boot_mode ? 1 : 0),
155 kim,
156 &kernel_sign_key)) {
157 continue;
158 }
159
160 /* Check for rollback of key version */
161 if (kim->kernel_key_version < tpm_kernel_key_version) {
162 RSAPublicKeyFree(kernel_sign_key);
163 continue;
164 }
165
166 /* Check for rollback of kernel version */
167 if (kim->kernel_key_version == tpm_kernel_key_version &&
168 kim->kernel_version < tpm_kernel_version) {
169 RSAPublicKeyFree(kernel_sign_key);
170 continue;
171 }
172
173 /* Check for lowest key version from a valid header. */
174 if (lowest_kernel_key_version > kim->kernel_key_version) {
175 lowest_kernel_key_version = kim->kernel_key_version;
176 lowest_kernel_version = kim->kernel_version;
177 }
178 else if (lowest_kernel_key_version == kim->kernel_key_version &&
179 lowest_kernel_version > kim->kernel_version) {
180 lowest_kernel_version = kim->kernel_version;
181 }
182
183 /* Verify kernel padding is a multiple of sector size. */
184 if (0 != kim->padded_header_size % blba) {
185 RSAPublicKeyFree(kernel_sign_key);
186 continue;
187 }
188
189 kernel_start = part_start + (kim->padded_header_size / blba);
190 kernel_sectors = (kim->kernel_len + blba - 1) / blba;
191
192 /* Read the kernel data */
193 if (0 != BootDeviceReadLBA(kernel_start, kernel_sectors,
194 params->kernel_buffer)) {
195 RSAPublicKeyFree(kernel_sign_key);
196 continue;
197 }
198
199 /* Verify kernel data */
200 if (0 != VerifyKernelData(kernel_sign_key,
201 kim->kernel_signature,
202 params->kernel_buffer,
203 kim->kernel_len,
204 kim->kernel_sign_algorithm)) {
205 RSAPublicKeyFree(kernel_sign_key);
206 continue;
207 }
208
209 /* Done with the kernel signing key, so can free it now */
210 RSAPublicKeyFree(kernel_sign_key);
211
212 /* If we're still here, the kernel is valid. */
213 /* Save the first good partition we find; that's the one we'll boot */
214 if (-1 == good_partition) {
215 good_partition = gpt.current_kernel;
216 params->partition_number = gpt.current_kernel;
217 params->bootloader_start = (uint8_t*)params->kernel_buffer +
218 kim->bootloader_offset;
219 params->bootloader_size = kim->bootloader_size;
220
221 /* If the good partition's key version is the same as the tpm, then
222 * the TPM doesn't need updating; we can stop now. Otherwise, we'll
223 * check all the other headers to see if they contain a newer key. */
224 if (kim->kernel_key_version == tpm_kernel_key_version &&
225 kim->kernel_version == tpm_kernel_version)
226 break;
227 }
228 } /* while(GptNextKernelEntry) */
229 } while(0);
230
231 /* Free kernel work and image buffers */
232 if (kbuf)
233 Free(kbuf);
234 if (kim)
235 Free(kim);
236
237 // Write and free GPT data
238 WriteAndFreeGptData(&gpt);
239
240 // Handle finding a good partition
241 if (good_partition >= 0) {
242
243 /* See if we need to update the TPM */
244 if (lowest_kernel_key_version > tpm_kernel_key_version) {
245 WriteStoredVersion(KERNEL_KEY_VERSION, lowest_kernel_key_version);
246 WriteStoredVersion(KERNEL_VERSION, lowest_kernel_version);
247 }
248 else if (lowest_kernel_key_version == tpm_kernel_key_version &&
249 lowest_kernel_version > tpm_kernel_version) {
250 WriteStoredVersion(KERNEL_VERSION, lowest_kernel_version);
251 }
252
253 if (BOOT_MODE_RECOVERY != params->boot_mode) {
254 /* We can lock the TPM now, since we've decided which kernel we
255 * like. If we don't find a good kernel, we leave the TPM
256 * unlocked so we can try again on the next boot device. If no
257 * kernels are good, we'll reboot to recovery mode, so it's ok to
258 * leave the TPM unlocked in that case too.
259 *
260 * If we're already in recovery mode, we need to leave PP unlocked,
261 * so don't lock the kernel versions. */
262 LockKernelVersionsByLockingPP();
263 }
264
265 /* Success! */
266 return LOAD_KERNEL_SUCCESS;
267 }
268
269 // Handle error cases
270 if (found_partition)
271 return LOAD_KERNEL_INVALID;
272 else
273 return LOAD_KERNEL_NOT_FOUND;
274 /* TODO: no error code for "internal error", but what would the firmware do
275 * with that anyway? So in the do-while(0) code above, the firmware just
276 * does 'break' to indicate an internal error... */
277 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698