Skip to content

Commit

Permalink
write bootparams in memory for Linux & PVH protos
Browse files Browse the repository at this point in the history
This commit introduces a new configurator module that takes
boot parameters created in the VMM (boot_params / start_info
+ e820 map) and writes them into guest memory. The module is
meant to be extended in order to include *building* the boot
parameters as well.

Fixes rust-vmm#15

Signed-off-by: Alexandra Iordache <aghecen@amazon.com>
  • Loading branch information
Alexandra Iordache committed Mar 27, 2020
1 parent 2adddce commit a0681be
Show file tree
Hide file tree
Showing 7 changed files with 555 additions and 0 deletions.
29 changes: 29 additions & 0 deletions src/configurator/aarch64/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause

//! Traits and structs for configuring and loading boot parameters on `aarch64`.

#![cfg(target_arch = "aarch64")]

use std::error::Error as StdError;
use std::fmt;

/// Placeholder error type.
#[derive(Debug, PartialEq)]
pub enum Error {
/// Placeholder error value.
Placeholder,
}

impl StdError for Error {
fn description(&self) -> &str {
unimplemented!()
}
}

impl fmt::Display for Error {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
unimplemented!()
}
}
116 changes: 116 additions & 0 deletions src/configurator/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright © 2020, Oracle and/or its affiliates.
//
// Copyright (c) 2019 Intel Corporation. All rights reserved.
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Copyright 2017 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-BSD-3-Clause file.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause

//! Traits and structs for configuring and loading boot parameters.
//! - [BootConfigurator](trait.BootConfigurator.html): configure boot parameters.
//! - [LinuxBootConfigurator](linux/struct.LinuxBootConfigurator.html): Linux boot protocol
//! parameters configurator.
//! - [PvhBootConfigurator](pvh/struct.PvhBootConfigurator.html): PVH boot protocol parameters
//! configurator.

use vm_memory::{ByteValued, GuestAddress, GuestMemory};

use std::error::Error as StdError;
use std::fmt;

#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
mod x86_64;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub use x86_64::*;

#[cfg(target_arch = "aarch64")]
mod aarch64;
#[cfg(target_arch = "aarch64")]
pub use aarch64::Error as ArmError;

/// Errors specific to boot protocol configuration.
#[derive(Debug, PartialEq)]
pub enum Error {
/// Errors specific to the Linux boot protocol configuration.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Linux(linux::Error),
/// Errors specific to the PVH boot protocol configuration.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Pvh(pvh::Error),
/// Errors specific to device tree boot configuration.
#[cfg(target_arch = "aarch64")]
Arm(ArmError),
}

impl StdError for Error {
fn description(&self) -> &str {
use Error::*;
match self {
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Linux(ref e) => e.description(),
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
Pvh(ref e) => e.description(),
#[cfg(target_arch = "aarch64")]
Arm(ref e) => e.description(),
}
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Boot Configurator Error: {}",
StdError::description(self)
)
}
}

/// A specialized `Result` type for the boot configurator.
pub type Result<T> = std::result::Result<T, Error>;

/// Trait that defines interfaces for building (TBD) and configuring boot parameters.
///
/// Currently, this trait exposes a single function which writes user-provided boot parameters into
/// guest memory at the user-specified addresses. It's meant to be called after the kernel is
/// loaded and after the boot parameters are built externally (in the VMM).
///
/// This trait will be extended with additional functionality to build boot parameters.
pub trait BootConfigurator {
/// Writes the boot parameters (configured elsewhere) into guest memory.
///
/// The arguments are split into `header` and `sections` to accommodate different boot
/// protocols like Linux boot and PVH. In Linux boot, the e820 map could be considered as
/// `sections`, but it's already encapsulated in the `boot_params` and thus all the boot
/// parameters are passed through a single struct. In PVH, the memory map table is separated
/// from the `hvm_start_info` struct, therefore it's passed separately.
///
/// # Arguments
///
/// * `header` - header section of the boot parameters and address where to write it in guest
/// memory. The first element must be a POD struct that implements [`ByteValued`].
/// For the Linux protocol it's the [`boot_params`] struct, and for PVH the
/// [`hvm_start_info`] struct.
/// * `sections` - vector of sections that compose the boot parameters and address where to
/// write them in guest memory. Unused for the Linux protocol. For PVH, it's the
/// memory map table represented as a vector of [`hvm_memmap_table_entry`]. Must
/// be a `Vec` of POD data structs that implement [`ByteValued`].
/// * `guest_memory` - guest's physical memory.
///
/// [`boot_params`]: ../loader/bootparam/struct.boot_e820_entry.html
/// [`hvm_memmap_table_entry`]: ../loader/elf/start_info/struct.hvm_memmap_table_entry.html
/// [`hvm_start_info`]: ../loader/elf/start_info/struct.hvm_start_info.html
/// [`ByteValued`]: https://docs.rs/vm-memory/latest/vm_memory/bytes/trait.ByteValued.html
fn write_bootparams<T, S, M>(
header: (T, GuestAddress),
sections: Option<(Vec<S>, GuestAddress)>,
guest_memory: &M,
) -> Result<()>
where
T: ByteValued,
S: ByteValued,
M: GuestMemory;
}
153 changes: 153 additions & 0 deletions src/configurator/x86_64/linux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
// Copyright © 2020, Oracle and/or its affiliates.
//
// Copyright (c) 2019 Intel Corporation. All rights reserved.
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Copyright 2017 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-BSD-3-Clause file.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause

//! Traits and structs for configuring and loading boot parameters on `x86_64` using the Linux
//! boot protocol.

use vm_memory::{ByteValued, Bytes, GuestAddress, GuestMemory};

use super::super::{BootConfigurator, Error as BootConfiguratorError, Result};
use crate::loader::bootparam::boot_params;

use std::error::Error as StdError;
use std::fmt;
use std::mem;

/// Boot configurator for the Linux boot protocol.
pub struct LinuxBootConfigurator {}

/// Errors specific to the Linux boot protocol configuration.
#[derive(Debug, PartialEq)]
pub enum Error {
/// The zero page extends past the end of guest memory.
ZeroPagePastRamEnd,
/// Error writing to the zero page of guest memory.
ZeroPageSetup,
}

impl StdError for Error {
fn description(&self) -> &str {
use Error::*;
match self {
ZeroPagePastRamEnd => "The zero page extends past the end of guest memory.",
ZeroPageSetup => "Error writing to the zero page of guest memory.",
}
}
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Linux Boot Configurator Error: {}",
StdError::description(self)
)
}
}

impl From<Error> for BootConfiguratorError {
fn from(err: Error) -> Self {
BootConfiguratorError::Linux(err)
}
}

impl BootConfigurator for LinuxBootConfigurator {
/// Writes the boot parameters (configured elsewhere) into guest memory.
///
/// # Arguments
///
/// * `header` - boot parameters encapsulated in a [`boot_params`] struct.
/// * `sections` - unused.
/// * `guest_memory` - guest's physical memory.
///
/// [`boot_params`]: ../loader/bootparam/struct.boot_e820_entry.html
fn write_bootparams<T, S, M>(
header: (T, GuestAddress),
_sections: Option<(Vec<S>, GuestAddress)>,
guest_memory: &M,
) -> Result<()>
where
T: ByteValued,
S: ByteValued,
M: GuestMemory,
{
// The VMM has filled a `boot_params` struct and its e820 map.
// This will be written in guest memory at the zero page.
guest_memory
.checked_offset(header.1, mem::size_of::<boot_params>())
.ok_or(Error::ZeroPagePastRamEnd)?;
guest_memory
.write_obj(header.0, header.1)
.map_err(|_| Error::ZeroPageSetup)?;

Ok(())
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::mem;
use vm_memory::{Address, GuestAddress, GuestMemoryMmap};

const KERNEL_BOOT_FLAG_MAGIC: u16 = 0xaa55;
const KERNEL_HDR_MAGIC: u32 = 0x53726448;
const KERNEL_LOADER_OTHER: u8 = 0xff;
const KERNEL_MIN_ALIGNMENT_BYTES: u32 = 0x1000000;
const MEM_SIZE: u64 = 0x1000000;

fn create_guest_mem() -> GuestMemoryMmap {
GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), (MEM_SIZE as usize))]).unwrap()
}

fn build_bootparams_common() -> boot_params {
let mut params = boot_params::default();
params.hdr.boot_flag = KERNEL_BOOT_FLAG_MAGIC;
params.hdr.header = KERNEL_HDR_MAGIC;
params.hdr.kernel_alignment = KERNEL_MIN_ALIGNMENT_BYTES;
params.hdr.type_of_loader = KERNEL_LOADER_OTHER;
params
}

#[test]
fn test_configure_linux_boot() {
let zero_page_addr = GuestAddress(0x30000);

let params = build_bootparams_common();
// This is where we'd append e820 entries, cmdline, PCI, ACPI etc.

let guest_memory = create_guest_mem();

// Error case: boot params don't fit in guest memory (zero page address too close to end).
let bad_zeropg_addr = GuestAddress(
guest_memory.last_addr().raw_value() - mem::size_of::<boot_params>() as u64 + 1,
);
assert_eq!(
LinuxBootConfigurator::write_bootparams::<boot_params, boot_params, GuestMemoryMmap>(
(params, bad_zeropg_addr),
None,
&guest_memory,
)
.err(),
Some(Error::ZeroPagePastRamEnd.into()),
);

// Success case.
assert!(
LinuxBootConfigurator::write_bootparams::<boot_params, boot_params, GuestMemoryMmap>(
(params, zero_page_addr),
None,
&guest_memory
)
.is_ok()
);
}
}
17 changes: 17 additions & 0 deletions src/configurator/x86_64/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright © 2020, Oracle and/or its affiliates.
//
// Copyright (c) 2019 Intel Corporation. All rights reserved.
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
//
// Copyright 2017 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-BSD-3-Clause file.
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause

//! Traits and structs for configuring and loading boot parameters on `x86_64`.

#![cfg(any(target_arch = "x86", target_arch = "x86_64"))]

pub mod linux;
pub mod pvh;
Loading

0 comments on commit a0681be

Please sign in to comment.