diff options
author | Daniel Verkamp <dverkamp@chromium.org> | 2019-05-13 12:34:54 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2019-05-14 23:57:41 -0700 |
commit | 050af40382a5619278fc08eef7dfbde5591abd76 (patch) | |
tree | c8f4284dbe5b59e91b0f66b9466519cfeeccad24 /x86_64 | |
parent | 21accb31ac9056ab28930689ddd9dce185956258 (diff) | |
download | crosvm-050af40382a5619278fc08eef7dfbde5591abd76.tar crosvm-050af40382a5619278fc08eef7dfbde5591abd76.tar.gz crosvm-050af40382a5619278fc08eef7dfbde5591abd76.tar.bz2 crosvm-050af40382a5619278fc08eef7dfbde5591abd76.tar.lz crosvm-050af40382a5619278fc08eef7dfbde5591abd76.tar.xz crosvm-050af40382a5619278fc08eef7dfbde5591abd76.tar.zst crosvm-050af40382a5619278fc08eef7dfbde5591abd76.zip |
x86_64: support loading bzImage kernels
The current kernel loader expects an extracted ELF kernel; this adds complexity to the build and test process for the guest kernel, since the normal output of a Linux kernel build is a bzImage-format kernel. bzImage also supports compressed kernels, which are smaller on disk and potentially quicker to load, depending on disk and CPU speed. Add support for loading of bzImage-format kernels, and use the 64-bit boot protocol as described in the official Linux/x86 boot protocol: https://www.kernel.org/doc/Documentation/x86/boot.txt The existing ELF loader is kept for compatibility with shipping kernel images; if a kernel image doesn't have the ELF signature, it is passed to the bzImage loader as a fallback. BUG=None TEST=Boot bzImage and extracted ELF kernels on x86-64 Change-Id: I90be4cd597d15bc89e63f0f6cbc781c5c8c2eaeb Signed-off-by: Daniel Verkamp <dverkamp@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/1609969 Tested-by: kokoro <noreply+kokoro@google.com> Reviewed-by: Dylan Reid <dgreid@chromium.org> Reviewed-by: Zach Reizner <zachr@chromium.org>
Diffstat (limited to 'x86_64')
-rw-r--r-- | x86_64/src/bzimage.rs | 101 | ||||
-rw-r--r-- | x86_64/src/lib.rs | 24 |
2 files changed, 119 insertions, 6 deletions
diff --git a/x86_64/src/bzimage.rs b/x86_64/src/bzimage.rs new file mode 100644 index 0000000..16a7338 --- /dev/null +++ b/x86_64/src/bzimage.rs @@ -0,0 +1,101 @@ +// Copyright 2019 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. + +// Loader for bzImage-format Linux kernels as described in +// https://www.kernel.org/doc/Documentation/x86/boot.txt + +use std::fmt::{self, Display}; +use std::io::{Read, Seek, SeekFrom}; + +use sys_util::{GuestAddress, GuestMemory}; + +use crate::bootparam::boot_params; + +#[derive(Debug, PartialEq)] +pub enum Error { + BadSignature, + InvalidSetupSects, + InvalidSysSize, + ReadBootParams, + ReadKernelImage, + SeekBootParams, + SeekKernelStart, +} +pub type Result<T> = std::result::Result<T, Error>; + +impl std::error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::Error::*; + + let description = match self { + BadSignature => "bad kernel header signature", + InvalidSetupSects => "invalid setup_sects value", + InvalidSysSize => "invalid syssize value", + ReadBootParams => "unable to read boot_params", + ReadKernelImage => "unable to read kernel image", + SeekBootParams => "unable to seek to boot_params", + SeekKernelStart => "unable to seek to kernel start", + }; + + write!(f, "bzImage loader: {}", description) + } +} + +/// Loads a kernel from a bzImage to a slice +/// +/// # Arguments +/// +/// * `guest_mem` - The guest memory region the kernel is written to. +/// * `kernel_start` - The offset into `guest_mem` at which to load the kernel. +/// * `kernel_image` - Input bzImage. +pub fn load_bzimage<F>( + guest_mem: &GuestMemory, + kernel_start: GuestAddress, + kernel_image: &mut F, +) -> Result<(boot_params, u64)> +where + F: Read + Seek, +{ + let mut params: boot_params = Default::default(); + kernel_image + .seek(SeekFrom::Start(0)) + .map_err(|_| Error::SeekBootParams)?; + unsafe { + // read_struct is safe when reading a POD struct. It can be used and dropped without issue. + sys_util::read_struct(kernel_image, &mut params).map_err(|_| Error::ReadBootParams)?; + } + + // bzImage header signature "HdrS" + if params.hdr.header != 0x53726448 { + return Err(Error::BadSignature); + } + + let setup_sects = if params.hdr.setup_sects == 0 { + 4u64 + } else { + params.hdr.setup_sects as u64 + }; + + let kernel_offset = setup_sects + .checked_add(1) + .ok_or(Error::InvalidSetupSects)? + .checked_mul(512) + .ok_or(Error::InvalidSetupSects)?; + let kernel_size = (params.hdr.syssize as usize) + .checked_mul(16) + .ok_or(Error::InvalidSysSize)?; + + kernel_image + .seek(SeekFrom::Start(kernel_offset)) + .map_err(|_| Error::SeekKernelStart)?; + + // Load the whole kernel image to kernel_start + guest_mem + .read_to_memory(kernel_start, kernel_image, kernel_size) + .map_err(|_| Error::ReadKernelImage)?; + + Ok((params, kernel_start.offset() + kernel_size as u64)) +} diff --git a/x86_64/src/lib.rs b/x86_64/src/lib.rs index 455e0ea..23df5ea 100644 --- a/x86_64/src/lib.rs +++ b/x86_64/src/lib.rs @@ -46,6 +46,7 @@ unsafe impl data_model::DataInit for mpspec::mpc_table {} unsafe impl data_model::DataInit for mpspec::mpc_lintsrc {} unsafe impl data_model::DataInit for mpspec::mpf_intel {} +mod bzimage; mod cpuid; mod gdt; mod interrupts; @@ -91,6 +92,7 @@ pub enum Error { CreateVm(sys_util::Error), E820Configuration, KernelOffsetPastEnd, + LoadBzImage(bzimage::Error), LoadCmdline(kernel_loader::Error), LoadInitrd(arch::LoadImageError), LoadKernel(kernel_loader::Error), @@ -133,6 +135,7 @@ impl Display for Error { CreateVm(e) => write!(f, "failed to create VM: {}", e), E820Configuration => write!(f, "invalid e820 setup params"), KernelOffsetPastEnd => write!(f, "the kernel extends past the end of RAM"), + LoadBzImage(e) => write!(f, "error loading kernel bzImage: {}", e), LoadCmdline(e) => write!(f, "error loading command line: {}", e), LoadInitrd(e) => write!(f, "error loading initrd: {}", e), LoadKernel(e) => write!(f, "error loading Kernel: {}", e), @@ -181,6 +184,7 @@ fn configure_system( pci_irqs: Vec<(u32, PciInterruptPin)>, setup_data: Option<GuestAddress>, initrd: Option<(GuestAddress, usize)>, + mut params: boot_params, ) -> Result<()> { const EBDA_START: u64 = 0x0009fc00; const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55; @@ -195,8 +199,6 @@ fn configure_system( smbios::setup_smbios(guest_mem).map_err(Error::SetupSmbios)?; - let mut params: boot_params = Default::default(); - params.hdr.type_of_loader = KERNEL_LOADER_OTHER; params.hdr.boot_flag = KERNEL_BOOT_FLAG_MAGIC; params.hdr.header = KERNEL_HDR_MAGIC; @@ -353,7 +355,7 @@ impl arch::LinuxArch for X8664arch { // separate out load_kernel from other setup to get a specific error for // kernel loading - let kernel_end = Self::load_kernel(&mem, &mut components.kernel_image)?; + let (params, kernel_end) = Self::load_kernel(&mem, &mut components.kernel_image)?; Self::setup_system_memory( &mem, @@ -364,6 +366,7 @@ impl arch::LinuxArch for X8664arch { pci_irqs, components.android_fstab, kernel_end, + params, )?; Ok(RunnableLinuxVm { @@ -389,9 +392,16 @@ impl X8664arch { /// /// * `mem` - The memory to be used by the guest. /// * `kernel_image` - the File object for the specified kernel. - fn load_kernel(mem: &GuestMemory, mut kernel_image: &mut File) -> Result<u64> { - kernel_loader::load_kernel(mem, GuestAddress(KERNEL_START_OFFSET), &mut kernel_image) - .map_err(Error::LoadKernel) + fn load_kernel(mem: &GuestMemory, mut kernel_image: &mut File) -> Result<(boot_params, u64)> { + let elf_result = + kernel_loader::load_kernel(mem, GuestAddress(KERNEL_START_OFFSET), &mut kernel_image); + if elf_result == Err(kernel_loader::Error::InvalidElfMagicNumber) { + bzimage::load_bzimage(mem, GuestAddress(KERNEL_START_OFFSET), &mut kernel_image) + .map_err(Error::LoadBzImage) + } else { + let kernel_end = elf_result.map_err(Error::LoadKernel)?; + Ok((Default::default(), kernel_end)) + } } /// Configures the system memory space should be called once per vm before @@ -412,6 +422,7 @@ impl X8664arch { pci_irqs: Vec<(u32, PciInterruptPin)>, android_fstab: Option<File>, kernel_end: u64, + params: boot_params, ) -> Result<()> { kernel_loader::load_cmdline(mem, GuestAddress(CMDLINE_OFFSET), cmdline) .map_err(Error::LoadCmdline)?; @@ -462,6 +473,7 @@ impl X8664arch { pci_irqs, setup_data, initrd, + params, )?; Ok(()) } |