OLD | NEW |
1 /* Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 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 | 2 * Use of this source code is governed by a BSD-style license that can be |
3 * found in the LICENSE file. | 3 * found in the LICENSE file. |
4 * | 4 * |
5 * Functions for loading a kernel from disk. | 5 * Functions for loading a kernel from disk. |
6 * (Firmware portion) | 6 * (Firmware portion) |
7 */ | 7 */ |
8 | 8 |
9 #include "load_kernel_fw.h" | 9 #include "load_kernel_fw.h" |
10 | 10 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
56 e->ending_lba - e->starting_lba + 1); | 56 e->ending_lba - e->starting_lba + 1); |
57 printf("Hacking attributes for kernel partition %d\n", i); | 57 printf("Hacking attributes for kernel partition %d\n", i); |
58 #endif | 58 #endif |
59 | 59 |
60 SetEntryPriority(e, 2); | 60 SetEntryPriority(e, 2); |
61 SetEntrySuccessful(e, 1); | 61 SetEntrySuccessful(e, 1); |
62 } | 62 } |
63 } | 63 } |
64 | 64 |
65 | 65 |
66 int AllocAndReadGptData(GptData *gptdata) { | 66 /* Allocates and reads GPT data from the drive. The sector_bytes and |
67 /* Allocates and reads GPT data from the drive. The sector_bytes and | 67 * drive_sectors fields should be filled on input. The primary and |
68 * drive_sectors fields should be filled on input. The primary and | 68 * secondary header and entries are filled on output. |
69 * secondary header and entries are filled on output. | 69 * |
70 * | 70 * Returns 0 if successful, 1 if error. */ |
71 * Returns 0 if successful, 1 if error. */ | 71 int AllocAndReadGptData(GptData* gptdata) { |
72 | 72 |
73 uint64_t entries_sectors = GPT_ENTRIES_SIZE / gptdata->sector_bytes; | 73 uint64_t entries_sectors = GPT_ENTRIES_SIZE / gptdata->sector_bytes; |
74 | 74 |
75 /* No data to be written yet */ | 75 /* No data to be written yet */ |
76 gptdata->modified = 0; | 76 gptdata->modified = 0; |
77 | 77 |
78 /* Allocate all buffers */ | 78 /* Allocate all buffers */ |
79 gptdata->primary_header = (uint8_t*)Malloc(gptdata->sector_bytes); | 79 gptdata->primary_header = (uint8_t*)Malloc(gptdata->sector_bytes); |
80 gptdata->secondary_header = (uint8_t*)Malloc(gptdata->sector_bytes); | 80 gptdata->secondary_header = (uint8_t*)Malloc(gptdata->sector_bytes); |
81 gptdata->primary_entries = (uint8_t*)Malloc(GPT_ENTRIES_SIZE); | 81 gptdata->primary_entries = (uint8_t*)Malloc(GPT_ENTRIES_SIZE); |
(...skipping 11 matching lines...) Expand all Loading... |
93 if (0 != BootDeviceReadLBA(gptdata->drive_sectors - entries_sectors - 1, | 93 if (0 != BootDeviceReadLBA(gptdata->drive_sectors - entries_sectors - 1, |
94 entries_sectors, gptdata->secondary_entries)) | 94 entries_sectors, gptdata->secondary_entries)) |
95 return 1; | 95 return 1; |
96 if (0 != BootDeviceReadLBA(gptdata->drive_sectors - 1, | 96 if (0 != BootDeviceReadLBA(gptdata->drive_sectors - 1, |
97 1, gptdata->secondary_header)) | 97 1, gptdata->secondary_header)) |
98 return 1; | 98 return 1; |
99 | 99 |
100 return 0; | 100 return 0; |
101 } | 101 } |
102 | 102 |
103 void WriteAndFreeGptData(GptData *gptdata) { | 103 |
104 /* Writes any changes for the GPT data back to the drive, then frees the | 104 /* Writes any changes for the GPT data back to the drive, then frees |
105 * buffers. */ | 105 * the buffers. |
| 106 * |
| 107 * Returns 0 if successful, 1 if error. */ |
| 108 int WriteAndFreeGptData(GptData* gptdata) { |
106 | 109 |
107 uint64_t entries_sectors = GPT_ENTRIES_SIZE / gptdata->sector_bytes; | 110 uint64_t entries_sectors = GPT_ENTRIES_SIZE / gptdata->sector_bytes; |
108 | 111 |
109 if (gptdata->primary_header) { | 112 if (gptdata->primary_header) { |
110 if (gptdata->modified & GPT_MODIFIED_HEADER1) | 113 if (gptdata->modified & GPT_MODIFIED_HEADER1) { |
111 BootDeviceWriteLBA(1, 1, gptdata->primary_header); | 114 if (0 != BootDeviceWriteLBA(1, 1, gptdata->primary_header)) |
| 115 return 1; |
| 116 } |
112 Free(gptdata->primary_header); | 117 Free(gptdata->primary_header); |
113 } | 118 } |
114 | 119 |
115 if (gptdata->primary_entries) { | 120 if (gptdata->primary_entries) { |
116 if (gptdata->modified & GPT_MODIFIED_ENTRIES1) | 121 if (gptdata->modified & GPT_MODIFIED_ENTRIES1) { |
117 BootDeviceWriteLBA(2, entries_sectors, gptdata->primary_entries); | 122 if (0 != BootDeviceWriteLBA(2, entries_sectors, |
| 123 gptdata->primary_entries)) |
| 124 return 1; |
| 125 } |
118 Free(gptdata->primary_entries); | 126 Free(gptdata->primary_entries); |
119 } | 127 } |
120 | 128 |
121 if (gptdata->secondary_entries) { | 129 if (gptdata->secondary_entries) { |
122 if (gptdata->modified & GPT_MODIFIED_ENTRIES2) | 130 if (gptdata->modified & GPT_MODIFIED_ENTRIES2) { |
123 BootDeviceWriteLBA(gptdata->drive_sectors - entries_sectors - 1, | 131 if (0 != BootDeviceWriteLBA(gptdata->drive_sectors - entries_sectors - 1, |
124 entries_sectors, gptdata->secondary_entries); | 132 entries_sectors, gptdata->secondary_entries)) |
| 133 return 1; |
| 134 } |
125 Free(gptdata->secondary_entries); | 135 Free(gptdata->secondary_entries); |
126 } | 136 } |
127 | 137 |
128 if (gptdata->secondary_header) { | 138 if (gptdata->secondary_header) { |
129 if (gptdata->modified & GPT_MODIFIED_HEADER2) | 139 if (gptdata->modified & GPT_MODIFIED_HEADER2) { |
130 BootDeviceWriteLBA(gptdata->drive_sectors - entries_sectors - 1, | 140 if (0 != BootDeviceWriteLBA(gptdata->drive_sectors - 1, 1, |
131 1, gptdata->secondary_header); | 141 gptdata->secondary_header)) |
132 BootDeviceWriteLBA(gptdata->drive_sectors - 1, 1, | 142 return 1; |
133 gptdata->secondary_header); | 143 } |
134 Free(gptdata->secondary_header); | 144 Free(gptdata->secondary_header); |
135 } | 145 } |
136 /* TODO: What to do with return codes from the writes? */ | 146 |
| 147 /* Success */ |
| 148 return 0; |
137 } | 149 } |
138 | 150 |
| 151 |
139 #define KBUF_SIZE 65536 /* Bytes to read at start of kernel partition */ | 152 #define KBUF_SIZE 65536 /* Bytes to read at start of kernel partition */ |
140 | 153 |
141 int LoadKernel(LoadKernelParams* params) { | 154 int LoadKernel(LoadKernelParams* params) { |
142 | 155 |
143 GptData gpt; | 156 GptData gpt; |
144 uint64_t part_start, part_size; | 157 uint64_t part_start, part_size; |
145 uint64_t blba = params->bytes_per_lba; | 158 uint64_t blba = params->bytes_per_lba; |
146 uint8_t* kbuf = NULL; | 159 uint8_t* kbuf = NULL; |
147 uint64_t kbuf_sectors; | 160 uint64_t kbuf_sectors; |
148 int found_partition = 0; | 161 int found_partition = 0; |
149 int good_partition = -1; | 162 int good_partition = -1; |
150 uint16_t tpm_kernel_key_version, tpm_kernel_version; | 163 uint16_t tpm_kernel_key_version, tpm_kernel_version; |
151 uint16_t lowest_kernel_key_version = 0xFFFF; | 164 uint16_t lowest_kernel_key_version = 0xFFFF; |
152 uint16_t lowest_kernel_version = 0xFFFF; | 165 uint16_t lowest_kernel_version = 0xFFFF; |
153 KernelImage *kim = NULL; | 166 KernelImage *kim = NULL; |
154 | 167 |
155 /* Clear output params in case we fail */ | 168 /* Clear output params in case we fail */ |
156 params->partition_number = 0; | 169 params->partition_number = 0; |
157 params->bootloader_address = 0; | 170 params->bootloader_address = 0; |
158 params->bootloader_size = 0; | 171 params->bootloader_size = 0; |
159 | 172 |
160 /* Read current kernel key index from TPM. Assumes TPM is already | 173 if (BOOT_MODE_NORMAL == params->boot_mode) { |
161 * initialized. */ | 174 /* Read current kernel key index from TPM. Assumes TPM is already |
162 /* TODO: Is that a safe assumption? Normally, SetupTPM() would be called | 175 * initialized. */ |
163 * when the RW firmware is verified. Is it harmful to call SetupTPM() | 176 if (0 != GetStoredVersions(KERNEL_VERSIONS, |
164 * again if it's already initialized? It'd be easier if we could just do | 177 &tpm_kernel_key_version, |
165 * that. */ | 178 &tpm_kernel_version)) |
166 GetStoredVersions(KERNEL_VERSIONS, | 179 return LOAD_KERNEL_RECOVERY; |
167 &tpm_kernel_key_version, | 180 } |
168 &tpm_kernel_version); | |
169 | 181 |
170 do { | 182 do { |
171 /* Read GPT data */ | 183 /* Read GPT data */ |
172 gpt.sector_bytes = blba; | 184 gpt.sector_bytes = blba; |
173 gpt.drive_sectors = params->ending_lba + 1; | 185 gpt.drive_sectors = params->ending_lba + 1; |
174 if (0 != AllocAndReadGptData(&gpt)) | 186 if (0 != AllocAndReadGptData(&gpt)) |
175 break; | 187 break; |
176 | 188 |
177 /* Initialize GPT library */ | 189 /* Initialize GPT library */ |
178 if (GPT_SUCCESS != GptInit(&gpt)) | 190 if (GPT_SUCCESS != GptInit(&gpt)) |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
283 RSAPublicKeyFree(kernel_sign_key); | 295 RSAPublicKeyFree(kernel_sign_key); |
284 | 296 |
285 /* If we're still here, the kernel is valid. */ | 297 /* If we're still here, the kernel is valid. */ |
286 /* Save the first good partition we find; that's the one we'll boot */ | 298 /* Save the first good partition we find; that's the one we'll boot */ |
287 if (-1 == good_partition) { | 299 if (-1 == good_partition) { |
288 good_partition = gpt.current_kernel; | 300 good_partition = gpt.current_kernel; |
289 params->partition_number = gpt.current_kernel; | 301 params->partition_number = gpt.current_kernel; |
290 params->bootloader_address = kim->bootloader_offset; | 302 params->bootloader_address = kim->bootloader_offset; |
291 params->bootloader_size = kim->bootloader_size; | 303 params->bootloader_size = kim->bootloader_size; |
292 | 304 |
293 /* If the good partition's key version is the same as the tpm, then | 305 /* If we're in developer or recovery mode, there's no rollback |
294 * the TPM doesn't need updating; we can stop now. Otherwise, we'll | 306 * protection, so we can stop at the first valid kernel. */ |
295 * check all the other headers to see if they contain a newer key. */ | 307 if (BOOT_MODE_NORMAL != params->boot_mode) |
| 308 break; |
| 309 |
| 310 /* Otherwise, we're in normal boot mode, so we do care about |
| 311 * the key index in the TPM. If the good partition's key |
| 312 * version is the same as the tpm, then the TPM doesn't need |
| 313 * updating; we can stop now. Otherwise, we'll check all the |
| 314 * other headers to see if they contain a newer key. */ |
296 if (kim->kernel_key_version == tpm_kernel_key_version && | 315 if (kim->kernel_key_version == tpm_kernel_key_version && |
297 kim->kernel_version == tpm_kernel_version) | 316 kim->kernel_version == tpm_kernel_version) |
298 break; | 317 break; |
299 } | 318 } |
300 } /* while(GptNextKernelEntry) */ | 319 } /* while(GptNextKernelEntry) */ |
301 } while(0); | 320 } while(0); |
302 | 321 |
303 /* Free kernel work and image buffers */ | 322 /* Free kernel work and image buffers */ |
304 if (kbuf) | 323 if (kbuf) |
305 Free(kbuf); | 324 Free(kbuf); |
306 if (kim) | 325 if (kim) |
307 Free(kim); | 326 Free(kim); |
308 | 327 |
309 // Write and free GPT data | 328 /* Write and free GPT data */ |
310 WriteAndFreeGptData(&gpt); | 329 WriteAndFreeGptData(&gpt); |
311 | 330 |
312 // Handle finding a good partition | 331 /* Handle finding a good partition */ |
313 if (good_partition >= 0) { | 332 if (good_partition >= 0) { |
314 | 333 |
315 /* See if we need to update the TPM */ | 334 if (BOOT_MODE_NORMAL == params->boot_mode) { |
316 if ((lowest_kernel_key_version > tpm_kernel_key_version) || | 335 /* See if we need to update the TPM, for normal boot mode only. */ |
317 (lowest_kernel_key_version == tpm_kernel_key_version && | 336 if ((lowest_kernel_key_version > tpm_kernel_key_version) || |
318 lowest_kernel_version > tpm_kernel_version)) { | 337 (lowest_kernel_key_version == tpm_kernel_key_version && |
319 WriteStoredVersions(KERNEL_VERSIONS, | 338 lowest_kernel_version > tpm_kernel_version)) { |
320 lowest_kernel_key_version, | 339 if (0 != WriteStoredVersions(KERNEL_VERSIONS, |
321 lowest_kernel_version); | 340 lowest_kernel_key_version, |
| 341 lowest_kernel_version)) |
| 342 return LOAD_KERNEL_RECOVERY; |
| 343 } |
322 } | 344 } |
323 | 345 |
324 if (BOOT_MODE_RECOVERY != params->boot_mode) { | 346 if (BOOT_MODE_RECOVERY != params->boot_mode) { |
325 /* We can lock the TPM now, since we've decided which kernel we | 347 /* We can lock the TPM now, since we've decided which kernel we |
326 * like. If we don't find a good kernel, we leave the TPM | 348 * like. If we don't find a good kernel, we leave the TPM |
327 * unlocked so we can try again on the next boot device. If no | 349 * unlocked so we can try again on the next boot device. If no |
328 * kernels are good, we'll reboot to recovery mode, so it's ok to | 350 * kernels are good, we'll reboot to recovery mode, so it's ok to |
329 * leave the TPM unlocked in that case too. | 351 * leave the TPM unlocked in that case too. |
330 * | 352 * |
331 * If we're already in recovery mode, we need to leave PP unlocked, | 353 * If we're already in recovery mode, we need to leave PP unlocked, |
332 * so don't lock the kernel versions. */ | 354 * so don't lock the kernel versions. */ |
333 LockKernelVersionsByLockingPP(); | 355 if (0 != LockKernelVersionsByLockingPP()) |
| 356 return LOAD_KERNEL_RECOVERY; |
334 } | 357 } |
335 | 358 |
336 /* Success! */ | 359 /* Success! */ |
337 return LOAD_KERNEL_SUCCESS; | 360 return LOAD_KERNEL_SUCCESS; |
338 } | 361 } |
339 | 362 |
340 // Handle error cases | 363 /* Handle error cases */ |
341 if (found_partition) | 364 if (found_partition) |
342 return LOAD_KERNEL_INVALID; | 365 return LOAD_KERNEL_INVALID; |
343 else | 366 else |
344 return LOAD_KERNEL_NOT_FOUND; | 367 return LOAD_KERNEL_NOT_FOUND; |
345 /* TODO: no error code for "internal error", but what would the firmware do | |
346 * with that anyway? So in the do-while(0) code above, the firmware just | |
347 * does 'break' to indicate an internal error... */ | |
348 } | 368 } |
OLD | NEW |