Chromium Code Reviews| Index: src/platform/vboot_reference/vkernel/kernel_image.c |
| diff --git a/src/platform/vboot_reference/vkernel/kernel_image.c b/src/platform/vboot_reference/vkernel/kernel_image.c |
| index 5eeb784f679599b12eba5d2b92099f1466e7c29a..e1d6a6900e9dcbd75b0127cdd63112e86dd8e699 100644 |
| --- a/src/platform/vboot_reference/vkernel/kernel_image.c |
| +++ b/src/platform/vboot_reference/vkernel/kernel_image.c |
| @@ -6,9 +6,8 @@ |
| * (Userland portion) |
| */ |
| -#include "kernel_image.h" |
| - |
| #include <fcntl.h> |
| +#include <stddef.h> |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| @@ -16,6 +15,8 @@ |
| #include "cryptolib.h" |
| #include "file_keys.h" |
| +#include "kernel_image.h" |
|
gauravsh
2010/05/27 18:06:04
oops, I meant only #include kernel_blob.h should g
|
| +#include "kernel_blob.h" |
| #include "rollback_index.h" |
| #include "signature_digest.h" |
| #include "utility.h" |
| @@ -32,6 +33,7 @@ KernelImage* KernelImageNew(void) { |
| image->preamble_signature = NULL; |
| image->kernel_signature = NULL; |
| image->kernel_data = NULL; |
| + image->padded_header_size = 0x4000; |
| } |
| return image; |
| } |
| @@ -47,9 +49,23 @@ void KernelImageFree(KernelImage* image) { |
| } |
| } |
| +uint64_t GetHeaderSizeOnDisk(const KernelImage* image) { |
| + uint64_t kernel_signature_len = siglen_map[image->kernel_sign_algorithm]; |
| + uint64_t kernel_key_signature_len = |
| + siglen_map[image->firmware_sign_algorithm]; |
| + |
| + return FIELD_LEN(magic) + |
| + GetKernelHeaderLen(image) + |
| + kernel_key_signature_len + |
| + GetKernelPreambleLen(image->kernel_sign_algorithm) + |
| + kernel_signature_len; |
| +} |
| + |
| + |
| KernelImage* ReadKernelImage(const char* input_file) { |
| uint64_t file_size; |
| - int image_len = 0; /* Total size of the kernel image. */ |
| + uint64_t on_disk_header_size; |
| + uint64_t on_disk_padding; |
| int header_len = 0; |
| int firmware_sign_key_len; |
| int kernel_key_signature_len; |
| @@ -64,9 +80,8 @@ KernelImage* ReadKernelImage(const char* input_file) { |
| return NULL; |
| kernel_buf = BufferFromFile(input_file, &file_size); |
| - image_len = file_size; |
| - st.remaining_len = image_len; |
| + st.remaining_len = file_size; |
| st.remaining_buf = kernel_buf; |
| st.overrun = 0; |
| @@ -141,11 +156,21 @@ KernelImage* ReadKernelImage(const char* input_file) { |
| StatefulMemcpy(&st, &image->padded_header_size, |
| FIELD_LEN(padded_header_size)); |
| - /* Read config and kernel signatures. */ |
| - image->preamble_signature = (uint8_t*) Malloc(kernel_signature_len); |
| - StatefulMemcpy(&st, image->preamble_signature, kernel_signature_len); |
| + /* Read preamble and kernel signatures. */ |
| image->kernel_signature = (uint8_t*) Malloc(kernel_signature_len); |
| StatefulMemcpy(&st, image->kernel_signature, kernel_signature_len); |
| + image->preamble_signature = (uint8_t*) Malloc(kernel_signature_len); |
| + StatefulMemcpy(&st, image->preamble_signature, kernel_signature_len); |
| + |
| + /* Skip over the rest of the padded header, unless we're already past it. */ |
| + on_disk_header_size = file_size - st.remaining_len; |
| + if (image->padded_header_size > on_disk_header_size) { |
| + on_disk_padding = image->padded_header_size - on_disk_header_size; |
| + if (st.remaining_len < on_disk_padding) |
| + st.overrun = -1; |
| + st.remaining_buf += on_disk_padding; |
| + st.remaining_len -= on_disk_padding; |
| + } |
| /* Read kernel image data. */ |
| image->kernel_data = (uint8_t*) Malloc(image->kernel_len); |
| @@ -250,22 +275,21 @@ uint8_t* GetKernelBlob(const KernelImage* image, uint64_t* blob_len) { |
| uint8_t* kernel_blob = NULL; |
| uint8_t* header_blob = NULL; |
| MemcpyState st; |
| + uint64_t on_disk_header_size; |
| + uint64_t on_disk_padding = 0; |
| if (!image) |
| return NULL; |
| kernel_key_signature_len = siglen_map[image->firmware_sign_algorithm]; |
| kernel_signature_len = siglen_map[image->kernel_sign_algorithm]; |
| - *blob_len = (FIELD_LEN(magic) + |
| - GetKernelHeaderLen(image) + |
| - kernel_key_signature_len + |
| - GetKernelPreambleLen(image->kernel_sign_algorithm) + |
| - kernel_signature_len + |
| - image->kernel_len); |
| + on_disk_header_size = GetHeaderSizeOnDisk(image); |
| + if (image->padded_header_size > on_disk_header_size) |
| + on_disk_padding = image->padded_header_size - on_disk_header_size; |
| + *blob_len = on_disk_header_size + on_disk_padding + image->kernel_len; |
| kernel_blob = (uint8_t*) Malloc(*blob_len); |
| st.remaining_len = *blob_len; |
| st.remaining_buf = kernel_blob; |
| st.overrun = 0; |
| - |
| header_blob = GetKernelHeaderBlob(image); |
| StatefulMemcpy_r(&st, image->magic, FIELD_LEN(magic)); |
| @@ -281,6 +305,9 @@ uint8_t* GetKernelBlob(const KernelImage* image, uint64_t* blob_len) { |
| FIELD_LEN(padded_header_size)); |
| StatefulMemcpy_r(&st, image->kernel_signature, kernel_signature_len); |
| StatefulMemcpy_r(&st, image->preamble_signature, kernel_signature_len); |
| + /* Copy a bunch of zeros to pad out the header */ |
| + if (on_disk_padding) |
| + StatefulMemset_r(&st, 0, on_disk_padding); |
| StatefulMemcpy_r(&st, image->kernel_data, image->kernel_len); |
| Free(header_blob); |
| @@ -293,7 +320,7 @@ uint8_t* GetKernelBlob(const KernelImage* image, uint64_t* blob_len) { |
| return kernel_blob; |
| } |
| -int WriteKernelImage(const char* input_file, |
| +int WriteKernelImage(const char* output_file, |
| const KernelImage* image, |
| int is_only_vblock) { |
| int fd; |
| @@ -303,9 +330,9 @@ int WriteKernelImage(const char* input_file, |
| if (!image) |
| return 0; |
| - if (-1 == (fd = creat(input_file, S_IRWXU))) { |
| + if (-1 == (fd = creat(output_file, S_IRWXU))) { |
| debug("Couldn't open file for writing kernel image: %s\n", |
| - input_file); |
| + output_file); |
| return 0; |
| } |
| kernel_blob = GetKernelBlob(image, &blob_len); |
| @@ -316,7 +343,7 @@ int WriteKernelImage(const char* input_file, |
| if (!is_only_vblock) { |
| if (blob_len != write(fd, kernel_blob, blob_len)) { |
| debug("Couldn't write Kernel Image to file: %s\n", |
| - input_file); |
| + output_file); |
| success = 0; |
| } |
| } else { |
| @@ -324,7 +351,7 @@ int WriteKernelImage(const char* input_file, |
| int vblock_len = blob_len - (image->kernel_len); |
| if (vblock_len != write(fd, kernel_blob, vblock_len)) { |
| debug("Couldn't write Kernel Image Verification block to file: %s\n", |
| - input_file); |
| + output_file); |
| success = 0; |
| } |
| } |
| @@ -334,9 +361,16 @@ int WriteKernelImage(const char* input_file, |
| } |
| void PrintKernelImage(const KernelImage* image) { |
| + uint64_t header_size; |
| + |
| if (!image) |
| return; |
| + header_size = GetHeaderSizeOnDisk(image); |
| + if (image->padded_header_size > header_size) |
| + header_size = image->padded_header_size; |
| + |
| + |
| /* Print header. */ |
| printf("Header Version = %d\n" |
| "Header Length = %d\n" |
| @@ -351,15 +385,17 @@ void PrintKernelImage(const KernelImage* image) { |
| /* TODO(gauravsh): Output hash and key signature here? */ |
| /* Print preamble. */ |
| printf("Kernel Version = %d\n" |
| - "kernel Length = %" PRId64 "\n" |
| - "Bootloader Offset = %" PRId64 "\n" |
| - "Bootloader Size = %" PRId64 "\n" |
| - "Padded Header Size = %" PRId64 "\n", |
| + "kernel Length = %" PRId64 " (0x%" PRIx64 ")\n" |
| + "Bootloader Offset = %" PRId64 " (0x%" PRIx64 ")\n" |
| + "Bootloader Size = %" PRId64 " (0x%" PRIx64 ")\n" |
| + "Padded Header Size = %" PRId64 " (0x%" PRIx64 ")\n\n" |
| + "Actual Header Size on disk = %" PRIu64 " (0x%" PRIx64 ")\n", |
| image->kernel_version, |
| - image->kernel_len, |
| - image->bootloader_offset, |
| - image->bootloader_size, |
| - image->padded_header_size); |
| + image->kernel_len, image->kernel_len, |
| + image->bootloader_offset, image->bootloader_offset, |
| + image->bootloader_size, image->bootloader_size, |
| + image->padded_header_size, image->padded_header_size, |
| + header_size, header_size); |
| /* TODO(gauravsh): Output kernel signature here? */ |
| } |
| @@ -547,3 +583,150 @@ void PrintKernelEntry(kernel_entry* entry) { |
| debug("Boot Tries Remaining = %d\n", entry->boot_tries_remaining); |
| debug("Boot Success Flag = %d\n", entry->boot_success_flag); |
| } |
| + |
| +// Return the smallest integral multiple of [alignment] that is equal to or |
| +// greater than [val]. Used to determine the number of |
| +// pages/sectors/blocks/whatever needed to contain [val] items/bytes/etc. |
| +static uint64_t roundup(uint64_t val, uint64_t alignment) { |
| + uint64_t rem = val % alignment; |
| + if ( rem ) |
| + return val + (alignment - rem); |
| + return val; |
| +} |
| + |
| +// Match regexp /\b--\b/ to delimit the start of the kernel commandline. If we |
| +// don't find one, we'll use the whole thing. |
| +static unsigned int find_cmdline_start(char *input, unsigned int max_len) { |
| + int start = 0; |
| + int i; |
| + for(i = 0; i < max_len-1 && input[i]; i++) { |
| + if (input[i] == '-' && input[i+1] == '-') { // found a "--" |
| + if ((i == 0 || input[i-1] == ' ') && // nothing before it |
| + (i+2 >= max_len || input[i+2] == ' ')) { // nothing after it |
| + start = i+2; // note: hope there's a trailing '\0' |
| + break; |
| + } |
| + } |
| + } |
| + while(input[start] == ' ') // skip leading spaces |
| + start++; |
| + |
| + return start; |
| +} |
| + |
| +uint8_t* GenerateKernelBlob(const char* kernel_file, |
| + const char* config_file, |
| + const char* bootloader_file, |
| + uint64_t* ret_blob_len, |
| + uint64_t* ret_bootloader_offset, |
| + uint64_t* ret_bootloader_size) { |
| + uint8_t* kernel_buf; |
| + uint8_t* config_buf; |
| + uint8_t* bootloader_buf; |
| + uint8_t* blob = 0; |
| + uint64_t kernel_size; |
| + uint64_t config_size; |
| + uint64_t bootloader_size; |
| + uint64_t blob_size; |
| + uint64_t kernel32_start = 0; |
| + uint64_t kernel32_size = 0; |
| + uint64_t bootloader_mem_start; |
| + uint64_t bootloader_mem_size; |
| + uint64_t now; |
| + struct linux_kernel_header *lh = 0; |
| + struct linux_kernel_params *params = 0; |
| + uint32_t cmdline_addr; |
| + uint64_t i; |
| + |
| + // Read the input files. |
| + kernel_buf = BufferFromFile(kernel_file, &kernel_size); |
| + if (!kernel_buf) |
| + goto done0; |
| + |
| + config_buf = BufferFromFile(config_file, &config_size); |
| + if (!config_buf) |
| + goto done1; |
| + if (config_size < CROS_CONFIG_SIZE) // need room for trailing '\0' |
| + goto done1; |
| + |
| + // Replace any newlines with spaces in the config file. |
| + for (i=0; i < config_size; i++) |
| + if (config_buf[i] == '\n') |
| + config_buf[i] = ' '; |
| + |
| + bootloader_buf = BufferFromFile(bootloader_file, &bootloader_size); |
| + if (!bootloader_buf) |
| + goto done2; |
| + |
| + // The first part of vmlinuz is a header, followed by a real-mode boot stub. |
| + // We only want the 32-bit part. |
| + if (kernel_size) { |
| + lh = (struct linux_kernel_header *)kernel_buf; |
| + kernel32_start = (lh->setup_sects+1) << 9; |
| + kernel32_size = kernel_size - kernel32_start; |
| + } |
| + |
| + // Allocate and zero the blob we need. |
| + blob_size = roundup(kernel32_size, CROS_ALIGN) + |
| + CROS_CONFIG_SIZE + |
| + CROS_PARAMS_SIZE + |
| + roundup(bootloader_size, CROS_ALIGN); |
| + blob = (uint8_t *)Malloc(blob_size); |
| + if (!blob) |
| + goto done3; |
| + Memset(blob, 0, blob_size); |
| + now = 0; |
| + |
| + // Copy the 32-bit kernel. |
| + if (kernel32_size) |
| + Memcpy(blob + now, kernel_buf + kernel32_start, kernel32_size); |
| + now += roundup(now + kernel32_size, CROS_ALIGN); |
| + |
| + // Find the load address of the commandline. We'll need it later. |
| + cmdline_addr = CROS_32BIT_ENTRY_ADDR + now |
| + + find_cmdline_start((char *)config_buf, config_size); |
| + |
| + // Copy the config. |
| + if (config_size) |
| + Memcpy(blob + now, config_buf, config_size); |
| + now += CROS_CONFIG_SIZE; |
| + |
| + // The zeropage data is next. Overlay the linux_kernel_header onto it, and |
| + // tweak a few fields. |
| + params = (struct linux_kernel_params *)(blob + now); |
| + |
| + if (kernel_size) |
| + Memcpy(&(params->setup_sects), &(lh->setup_sects), |
| + sizeof(*lh) - offsetof(struct linux_kernel_header, setup_sects)); |
| + params->boot_flag = 0; |
| + params->ramdisk_image = 0; // we don't support initrd |
| + params->ramdisk_size = 0; |
| + params->type_of_loader = 0xff; |
| + params->cmd_line_ptr = cmdline_addr; |
| + now += CROS_PARAMS_SIZE; |
| + |
| + // Finally, append the bootloader. Remember where it will load in memory, too. |
| + bootloader_mem_start = CROS_32BIT_ENTRY_ADDR + now; |
| + bootloader_mem_size = roundup(bootloader_size, CROS_ALIGN); |
| + if (bootloader_size) |
| + Memcpy(blob + now, bootloader_buf, bootloader_size); |
| + now += bootloader_mem_size; |
| + |
| + // Pass back some info. |
| + if (ret_blob_len) |
| + *ret_blob_len = blob_size; |
| + if (ret_bootloader_offset) |
| + *ret_bootloader_offset = bootloader_mem_start; |
| + if (ret_bootloader_size) |
| + *ret_bootloader_size = bootloader_mem_size; |
| + |
| + // Clean up and return the blob. |
| +done3: |
| + Free(bootloader_buf); |
| +done2: |
| + Free(config_buf); |
| +done1: |
| + Free(kernel_buf); |
| +done0: |
| + return blob; |
| +} |