diff --git a/Makefile b/Makefile index 08049720b7..dfa971cf30 100644 --- a/Makefile +++ b/Makefile @@ -454,6 +454,7 @@ HOSTLIB_SRCS = \ futility/dump_kernel_config_lib.c \ host/arch/${ARCH}/lib/crossystem_arch.c \ host/lib/crossystem.c \ + host/lib/extract_vmlinuz.c \ host/lib/fmap.c \ host/lib/host_misc.c @@ -478,7 +479,8 @@ TINYHOSTLIB_SRCS = \ firmware/stub/vboot_api_stub_disk.c \ firmware/stub/vboot_api_stub_sf.c \ firmware/stub/utility_stub.c \ - futility/dump_kernel_config_lib.c + futility/dump_kernel_config_lib.c \ + host/lib/extract_vmlinuz.c TINYHOSTLIB_OBJS = ${TINYHOSTLIB_SRCS:%.c=${BUILD}/%.o} diff --git a/host/include/vboot_host.h b/host/include/vboot_host.h index b22eea2d32..4b5db67ed9 100644 --- a/host/include/vboot_host.h +++ b/host/include/vboot_host.h @@ -58,5 +58,14 @@ char *FindKernelConfig(const char *filename, uint64_t kernel_body_load_address); /****************************************************************************/ +/* Kernel partition */ + +/* Used to get a bootable vmlinuz from the kernel partition. vmlinuz_out must + * be free'd after this function returns success. Success is indicated by a + * zero return value. + */ +int ExtractVmlinuz(void *kpart_data, size_t kpart_size, + void **vmlinuz_out, size_t *vmlinuz_size); + #endif /* VBOOT_HOST_H_ */ diff --git a/host/lib/extract_vmlinuz.c b/host/lib/extract_vmlinuz.c new file mode 100644 index 0000000000..81d3918ead --- /dev/null +++ b/host/lib/extract_vmlinuz.c @@ -0,0 +1,74 @@ +/* Copyright 2015 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. + * + * Exports a vmlinuz from a kernel partition in memory. + */ + +#include +#include + +#include "vboot_struct.h" + + +int ExtractVmlinuz(void *kpart_data, size_t kpart_size, + void **vmlinuz_out, size_t *vmlinuz_size) { + uint64_t now = 0; + VbKeyBlockHeader *keyblock = NULL; + VbKernelPreambleHeader *preamble = NULL; + uint8_t *kblob_data = NULL; + uint64_t kblob_size = 0; + uint64_t vmlinuz_header_size = 0; + uint64_t vmlinuz_header_address = 0; + uint64_t vmlinuz_header_offset = 0; + void *vmlinuz = NULL; + + keyblock = (VbKeyBlockHeader *)kpart_data; + now += keyblock->key_block_size; + if (now > kpart_size) + return 1; + + preamble = (VbKernelPreambleHeader *)(kpart_data + now); + now += preamble->preamble_size; + if (now > kpart_size) + return 1; + + kblob_data = kpart_data + now; + kblob_size = preamble->body_signature.data_size; + + if (!kblob_data || (now + kblob_size) > kpart_size) + return 1; + + if (preamble->header_version_minor > 0) { + vmlinuz_header_address = preamble->vmlinuz_header_address; + vmlinuz_header_size = preamble->vmlinuz_header_size; + } + + if (!vmlinuz_header_size || + kpart_data + vmlinuz_header_offset + vmlinuz_header_size > kpart_data) { + return 1; + } + + // calculate the vmlinuz_header offset from + // the beginning of the kpart_data. The kblob doesn't + // include the body_load_offset, but does include + // the keyblock and preamble sections. + vmlinuz_header_offset = vmlinuz_header_address - + preamble->body_load_address + + keyblock->key_block_size + + preamble->preamble_size; + + vmlinuz = malloc(vmlinuz_header_size + kblob_size); + if (vmlinuz == NULL) + return 1; + + memcpy(vmlinuz, kpart_data + vmlinuz_header_offset, + vmlinuz_header_size); + + memcpy(vmlinuz + vmlinuz_header_size, kblob_data, kblob_size); + + *vmlinuz_out = vmlinuz; + *vmlinuz_size = vmlinuz_header_size + kblob_size; + + return 0; +}