Modifying the kernel_utility tool to create our magic blob.

For the --generate operation, the --in <file> option is gone and there are
three new required options:

  --vmlinuz <file>         Embedded kernel image
  --config <file>          Embedded kernel command-line parameters
  --bootloader <file>      Embedded bootloader stub

This takes the specified kernel, extracts the 32-bit component, and combines
that with the configuration file (essentially just the kernel cmdline
string) and the bootstub image . The resulting blob is signed and ready to
put in a kernel partition.

There's also an optional --padding parameter, to specify how much extra
(unsigned) space to leave between the signature header and the kernel blob.
The default is 0x4000, which is about four times as much needed when using
the largest signature size we currently support.

Review URL: http://codereview.chromium.org/2283005
This commit is contained in:
Bill Richardson
2010-05-27 11:15:14 -07:00
parent 3003c1dd50
commit f5db4b86fa
8 changed files with 423 additions and 69 deletions

View File

@@ -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
}

View File

@@ -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_;

View File

@@ -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 <infile>\t\t\t\tSigned boot image to describe.\n"
"\n"
"For \"--verify\", required OPTIONS are:\n"
"--in <infile>\t\t\tVerified boot kernel image to verify.\n"
"--firmware_key_pub <pubkeyfile>\tPre-processed public firmware key "
"to use for verification.\n\n"
" --in <infile>\t\t\t\tSigned boot image to verify.\n"
" --firmware_key_pub <pubkeyfile>\tPre-processed public firmware key\n"
"\n"
"For \"--generate\", required OPTIONS are:\n"
"--firmware_key <privkeyfile>\tPrivate firmware signing key file\n"
"--kernel_key <privkeyfile>\tPrivate kernel signing key file\n"
"--kernel_key_pub <pubkeyfile>\tPre-processed public kernel signing"
" --firmware_key <privkeyfile>\t\tPrivate firmware signing key file\n"
" --kernel_key <privkeyfile>\t\tPrivate kernel signing key file\n"
" --kernel_key_pub <pubkeyfile>\t\tPre-processed public kernel signing"
" key\n"
"--firmware_sign_algorithm <algoid>\tSigning algorithm used by "
"the firmware\n"
"--kernel_sign_algorithm <algoid>\tSigning algorithm to use for kernel\n"
"--kernel_key_version <version#>\tKernel signing Key Version#\n"
"--kernel_version <version#>\tKernel Version#\n"
"--in <infile>\t\tKernel Image to sign\n"
"--out <outfile>\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 <algoid>\tSigning algorithm for firmware\n"
" --kernel_sign_algorithm <algoid>\tSigning algorithm for kernel\n"
" --kernel_key_version <number>\t\tKernel signing key version number\n"
" --kernel_version <number>\t\tKernel Version number\n"
" --config <file>\t\t\tEmbedded kernel command-line parameters\n"
" --bootloader <file>\t\t\tEmbedded bootloader stub\n"
" --vmlinuz <file>\t\t\tEmbedded kernel image\n"
" --out <outfile>\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"
"<algoid> (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<char**>(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<char**>(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<char**>(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<char**>(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;
}

View File

@@ -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_ */

View File

@@ -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
}

View File

@@ -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_

View File

@@ -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.
*/

View File

@@ -5,10 +5,10 @@
* Functions for generating and manipulating a verified boot kernel image.
* (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 +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;
}