diff --git a/tests/gen_fuzz_test_cases.sh b/tests/gen_fuzz_test_cases.sh index 7f75a31151..8cee42bd21 100755 --- a/tests/gen_fuzz_test_cases.sh +++ b/tests/gen_fuzz_test_cases.sh @@ -33,7 +33,6 @@ function generate_fuzzing_images { echo "Generating signed kernel test image..." # Generate a test verified boot kernel image and copy firmware public key. ${UTIL_DIR}/kernel_utility --generate \ - --in $1 \ --firmware_key ${TESTKEY_DIR}/key_rsa4096.pem \ --kernel_key ${TESTKEY_DIR}/key_rsa1024.pem \ --kernel_key_pub ${TESTKEY_DIR}/key_rsa1024.keyb \ @@ -41,12 +40,17 @@ function generate_fuzzing_images { --kernel_sign_algorithm 2 \ --kernel_key_version 1 \ --kernel_version 1 \ + --vmlinuz /dev/null \ + --config /dev/null \ + --bootloader ${TEST_FILE} \ --out ${TESTCASE_DIR}/kernel.signed cp ${TESTKEY_DIR}/key_rsa4096.keyb ${TESTCASE_DIR}/firmware_key.keyb } function pre_work { # Generate a file to serve as random bytes for firmware/kernel contents. + # NOTE: The kernel and config file can't really be random, but the bootloader + # can. That's probably close enough. echo "Generating test file..." dd if=/dev/urandom of=${TEST_FILE} bs=${TEST_FILE_SIZE} count=1 } diff --git a/utility/include/kernel_utility.h b/utility/include/kernel_utility.h index 5b0cab398c..1d03685aa5 100644 --- a/utility/include/kernel_utility.h +++ b/utility/include/kernel_utility.h @@ -58,6 +58,9 @@ class KernelUtility { std::string firmware_key_pub_file_; std::string kernel_key_file_; // Private key for signing the kernel. std::string kernel_key_pub_file_; + std::string config_file_; // File containing kernel commandline parameters + std::string bootloader_file_; // Embedded bootloader code + std::string vmlinuz_file_; // Input vmlinuz to be embedded in signed blob. // Fields of a KernelImage. (read from the command line). int header_version_; @@ -65,6 +68,7 @@ class KernelUtility { int kernel_sign_algorithm_; int kernel_key_version_; int kernel_version_; + int padding_; uint64_t kernel_len_; uint8_t* kernel_config_; diff --git a/utility/kernel_utility.cc b/utility/kernel_utility.cc index 7a1e66903e..99f637f60a 100644 --- a/utility/kernel_utility.cc +++ b/utility/kernel_utility.cc @@ -35,6 +35,7 @@ KernelUtility::KernelUtility(): image_(NULL), kernel_sign_algorithm_(-1), kernel_key_version_(-1), kernel_version_(-1), + padding_(0), kernel_len_(0), is_generate_(false), is_verify_(false), @@ -48,36 +49,46 @@ KernelUtility::~KernelUtility() { } void KernelUtility::PrintUsage(void) { - cerr << - "Utility to generate/verify/describe a verified boot kernel image\n\n" - "Usage: kernel_utility <--generate|--verify|--describe> [OPTIONS]\n\n" + cerr << "\n" + "Utility to generate/verify/describe a verified boot kernel image\n" + "\n" + "Usage: kernel_utility <--generate|--verify|--describe> [OPTIONS]\n" + "\n" + "For \"--describe\", the required OPTIONS are:\n" + " --in \t\t\t\tSigned boot image to describe.\n" + "\n" "For \"--verify\", required OPTIONS are:\n" - "--in \t\t\tVerified boot kernel image to verify.\n" - "--firmware_key_pub \tPre-processed public firmware key " - "to use for verification.\n\n" + " --in \t\t\t\tSigned boot image to verify.\n" + " --firmware_key_pub \tPre-processed public firmware key\n" + "\n" "For \"--generate\", required OPTIONS are:\n" - "--firmware_key \tPrivate firmware signing key file\n" - "--kernel_key \tPrivate kernel signing key file\n" - "--kernel_key_pub \tPre-processed public kernel signing" + " --firmware_key \t\tPrivate firmware signing key file\n" + " --kernel_key \t\tPrivate kernel signing key file\n" + " --kernel_key_pub \t\tPre-processed public kernel signing" " key\n" - "--firmware_sign_algorithm \tSigning algorithm used by " - "the firmware\n" - "--kernel_sign_algorithm \tSigning algorithm to use for kernel\n" - "--kernel_key_version \tKernel signing Key Version#\n" - "--kernel_version \tKernel Version#\n" - "--in \t\tKernel Image to sign\n" - "--out \t\tOutput file for verified boot Kernel image\n\n" - "Optional arguments for \"--generate\" include:\n" - "--vblock\t\t\tJust output the verification block\n\n" + " --firmware_sign_algorithm \tSigning algorithm for firmware\n" + " --kernel_sign_algorithm \tSigning algorithm for kernel\n" + " --kernel_key_version \t\tKernel signing key version number\n" + " --kernel_version \t\tKernel Version number\n" + " --config \t\t\tEmbedded kernel command-line parameters\n" + " --bootloader \t\t\tEmbedded bootloader stub\n" + " --vmlinuz \t\t\tEmbedded kernel image\n" + " --out \t\t\tOutput file for verified boot image\n" + "\n" + "Optional arguments for \"--generate\" are:\n" + " --vblock\t\t\t\tJust output the verification block\n" + " --padding\t\t\t\tPad the header to this size\n" + "\n" " (for --*_sign_algorithm) is one of the following:\n"; for (int i = 0; i < kNumAlgorithms; i++) { - cerr << i << " for " << algo_strings[i] << "\n"; + cerr << " " << i << " for " << algo_strings[i] << "\n"; } cerr << "\n\n"; } bool KernelUtility::ParseCmdLineOptions(int argc, char* argv[]) { int option_index, i; + char *e = 0; enum { OPT_FIRMWARE_KEY = 1000, OPT_FIRMWARE_KEY_PUB, @@ -93,6 +104,10 @@ bool KernelUtility::ParseCmdLineOptions(int argc, char* argv[]) { OPT_VERIFY, OPT_DESCRIBE, OPT_VBLOCK, + OPT_BOOTLOADER, + OPT_VMLINUZ, + OPT_CONFIG, + OPT_PADDING, }; static struct option long_options[] = { {"firmware_key", 1, 0, OPT_FIRMWARE_KEY }, @@ -109,6 +124,10 @@ bool KernelUtility::ParseCmdLineOptions(int argc, char* argv[]) { {"verify", 0, 0, OPT_VERIFY }, {"describe", 0, 0, OPT_DESCRIBE }, {"vblock", 0, 0, OPT_VBLOCK }, + {"bootloader", 1, 0, OPT_BOOTLOADER }, + {"vmlinuz", 1, 0, OPT_VMLINUZ }, + {"config", 1, 0, OPT_CONFIG }, + {"padding", 1, 0, OPT_PADDING }, {NULL, 0, 0, 0} }; while ((i = getopt_long(argc, argv, "", long_options, &option_index)) != -1) { @@ -129,32 +148,43 @@ bool KernelUtility::ParseCmdLineOptions(int argc, char* argv[]) { kernel_key_pub_file_ = optarg; break; case OPT_FIRMWARE_SIGN_ALGORITHM: - errno = 0; // strtol() returns an error via errno - firmware_sign_algorithm_ = strtol(optarg, - reinterpret_cast(NULL), 10); - if (errno) + firmware_sign_algorithm_ = strtol(optarg, &e, 0); + if (!*optarg || (e && *e)) { + cerr << "Invalid argument to --" + << long_options[option_index].name + << ": " << optarg << "\n"; return false; + } break; case OPT_KERNEL_SIGN_ALGORITHM: errno = 0; - kernel_sign_algorithm_ = strtol(optarg, - reinterpret_cast(NULL), 10); - if (errno) + kernel_sign_algorithm_ = strtol(optarg, &e, 0); + if (!*optarg || (e && *e)) { + cerr << "Invalid argument to --" + << long_options[option_index].name + << ": " << optarg << "\n"; return false; + } break; case OPT_KERNEL_KEY_VERSION: errno = 0; - kernel_key_version_ = strtol(optarg, - reinterpret_cast(NULL), 10); - if (errno) + kernel_key_version_ = strtol(optarg, &e, 0); + if (!*optarg || (e && *e)) { + cerr << "Invalid argument to --" + << long_options[option_index].name + << ": " << optarg << "\n"; return false; - break; + } + break; case OPT_KERNEL_VERSION: errno = 0; - kernel_version_ = strtol(optarg, - reinterpret_cast(NULL), 10); - if (errno) + kernel_version_ = strtol(optarg, &e, 0); + if (!*optarg || (e && *e)) { + cerr << "Invalid argument to --" + << long_options[option_index].name + << ": " << optarg << "\n"; return false; + } break; case OPT_IN: in_file_ = optarg; @@ -174,6 +204,24 @@ bool KernelUtility::ParseCmdLineOptions(int argc, char* argv[]) { case OPT_VBLOCK: is_only_vblock_ = true; break; + case OPT_BOOTLOADER: + bootloader_file_ = optarg; + break; + case OPT_VMLINUZ: + vmlinuz_file_ = optarg; + break; + case OPT_CONFIG: + config_file_ = optarg; + break; + case OPT_PADDING: + padding_ = strtol(optarg, &e, 0); + if (!*optarg || (e && *e)) { + cerr << "Invalid argument to --" + << long_options[option_index].name + << ": " << optarg << "\n"; + return false; + } + break; } } return CheckOptions(); @@ -205,6 +253,8 @@ bool KernelUtility::GenerateSignedImage(void) { // TODO(gauravsh): make this a command line option. image_->header_version = 1; + if (padding_) + image_->padded_header_size = padding_; image_->firmware_sign_algorithm = (uint16_t) firmware_sign_algorithm_; // Copy pre-processed public signing key. image_->kernel_sign_algorithm = (uint16_t) kernel_sign_algorithm_; @@ -221,8 +271,13 @@ bool KernelUtility::GenerateSignedImage(void) { CalculateKernelHeaderChecksum(image_, image_->header_checksum); image_->kernel_version = kernel_version_; - image_->kernel_data = BufferFromFile(in_file_.c_str(), - &image_->kernel_len); + + image_->kernel_data = GenerateKernelBlob(vmlinuz_file_.c_str(), + config_file_.c_str(), + bootloader_file_.c_str(), + &image_->kernel_len, + &image_->bootloader_offset, + &image_->bootloader_size); if (!image_->kernel_data) return false; // Generate and add the signatures. @@ -268,14 +323,23 @@ bool KernelUtility::CheckOptions(void) { return false; } // Common required options. - if (in_file_.empty()) { - cerr << "No input file specified.\n"; - return false; + // Required options for --describe. + if (is_describe_) { + if (in_file_.empty()) { + cerr << "No input file specified.\n"; + return false; + } } // Required options for --verify. - if (is_verify_ && firmware_key_pub_file_.empty()) { - cerr << "No pre-processed public firmware key file specified.\n"; - return false; + if (is_verify_) { + if (firmware_key_pub_file_.empty()) { + cerr << "No pre-processed public firmware key file specified.\n"; + return false; + } + if (in_file_.empty()) { + cerr << "No input file specified.\n"; + return false; + } } // Required options for --generate. if (is_generate_) { @@ -313,6 +377,18 @@ bool KernelUtility::CheckOptions(void) { cerr <<"No output file specified.\n"; return false; } + if (config_file_.empty()) { + cerr << "No config file specified.\n"; + return false; + } + if (bootloader_file_.empty()) { + cerr << "No bootloader file specified.\n"; + return false; + } + if (vmlinuz_file_.empty()) { + cerr << "No vmlinuz file specified.\n"; + return false; + } } return true; } diff --git a/vboot_firmware/include/utility.h b/vboot_firmware/include/utility.h index a5df8fa6ba..e4d9aa3833 100644 --- a/vboot_firmware/include/utility.h +++ b/vboot_firmware/include/utility.h @@ -85,4 +85,15 @@ void* StatefulMemcpy(MemcpyState* state, void* dst, uint64_t len); */ const void* StatefulMemcpy_r(MemcpyState* state, const void* src, uint64_t len); +/* Like StatefulMemcpy_r() but fills a portion of the encapsulated buffer with + * a constant value. + * On success, return a meaningless but non-NULL pointer and updates [state]. + * On failure, return NULL, set remaining_len in state to -1. + * + * After the first failure (buffer overrun), successive calls will always fail. + */ +const void* StatefulMemset_r(MemcpyState* state, const uint8_t val, + uint64_t len); + + #endif /* VBOOT_REFERENCE_UTILITY_H_ */ diff --git a/vboot_firmware/stub/utility_stub.c b/vboot_firmware/stub/utility_stub.c index 66132703ea..7a028e7bec 100644 --- a/vboot_firmware/stub/utility_stub.c +++ b/vboot_firmware/stub/utility_stub.c @@ -96,3 +96,17 @@ const void* StatefulMemcpy_r(MemcpyState* state, const void* src, state->remaining_len -= len; return src; } + +const void* StatefulMemset_r(MemcpyState* state, const uint8_t val, + uint64_t len) { + if (state->overrun) + return NULL; + if (len > state->remaining_len) { + state->overrun = 1; + return NULL; + } + Memset(state->remaining_buf, val, len); + state->remaining_buf += len; + state->remaining_len -= len; + return state; // have to return something non-NULL +} diff --git a/vkernel/include/kernel_blob.h b/vkernel/include/kernel_blob.h new file mode 100644 index 0000000000..d5256aa826 --- /dev/null +++ b/vkernel/include/kernel_blob.h @@ -0,0 +1,50 @@ +// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Constants describing the kernel blob content. + +#ifndef VBOOT_REFERENCE_KERNEL_BLOB_H_ +#define VBOOT_REFERENCE_KERNEL_BLOB_H_ + + +// Maximum kernel command-line size +#define CROS_CONFIG_SIZE 4096 + +// Size of the x86 zeropage table +#define CROS_PARAMS_SIZE 4096 + +// Alignment of various chunks within the kernel blob +#define CROS_ALIGN 4096 + +// RAM address where the 32-bit kernel expects to be started +#define CROS_32BIT_ENTRY_ADDR 0x100000 + +// Simplified version of the vmlinuz file header +struct linux_kernel_header +{ + uint8_t pad0[0x01f1 - 0x0]; + uint8_t setup_sects; // 1f1 + uint8_t pad1[0x0230 - 0x1f2]; +} __attribute__ ((packed)); + + +// Simplified version of the x86 kernel zeropage table +struct linux_kernel_params +{ + uint8_t pad0[0x01f1 - 0x0]; + uint8_t setup_sects; // 1f1 + uint8_t pad1[0x1fe - 0x1f2]; + uint16_t boot_flag; // 1fe + uint8_t pad2[0x210 - 0x200]; + uint8_t type_of_loader; // 210 + uint8_t pad3[0x218 - 0x211]; + uint32_t ramdisk_image; // 218 + uint32_t ramdisk_size; // 21c + uint8_t pad4[0x228 - 0x220]; + uint32_t cmd_line_ptr; // 228 + uint8_t pad5[0x0cd0 - 0x22c]; +} __attribute__ ((packed)); + + +#endif // VBOOT_REFERENCE_KERNEL_BLOB_H_ diff --git a/vkernel/include/kernel_image.h b/vkernel/include/kernel_image.h index 3fcdcf8bc5..58e1b1ac99 100644 --- a/vkernel/include/kernel_image.h +++ b/vkernel/include/kernel_image.h @@ -63,6 +63,18 @@ int WriteKernelImage(const char* input_file, const KernelImage* image, int is_only_vblock); +/* Create a kernel_data blob from its components and fill + * its length into blob_len, plus some information about the bootloader. + * + * Caller owns the returned pointer and must Free() it. + */ +uint8_t* GenerateKernelBlob(const char* vmlinuz_file, + const char* config_file, + const char* bootloader_file, + uint64_t* blob_len, + uint64_t* bootloader_offset, + uint64_t* bootloader_size); + /* Pretty print the contents of [image]. Only headers and metadata information * is printed. */ diff --git a/vkernel/kernel_image.c b/vkernel/kernel_image.c index 5eeb784f67..9dd3da5274 100644 --- a/vkernel/kernel_image.c +++ b/vkernel/kernel_image.c @@ -5,10 +5,10 @@ * Functions for generating and manipulating a verified boot kernel image. * (Userland portion) */ - #include "kernel_image.h" #include +#include #include #include #include @@ -16,6 +16,7 @@ #include "cryptolib.h" #include "file_keys.h" +#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; +}