mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2024-11-24 12:34:31 +00:00
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>
This commit is contained in:
parent
21accb31ac
commit
050af40382
2 changed files with 119 additions and 6 deletions
101
x86_64/src/bzimage.rs
Normal file
101
x86_64/src/bzimage.rs
Normal file
|
@ -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))
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue