| OLD | NEW | 
|---|
| (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 } | 
| OLD | NEW | 
|---|