Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Boot configurator: trait (and associated objects) that write boot params in guest memory #31

Merged
merged 5 commits into from
May 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion coverage_config_aarch64.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"coverage_score": 75.7,
"coverage_score": 80.3,
"exclude_path": "",
"crate_features": ""
}
2 changes: 1 addition & 1 deletion coverage_config_x86_64.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"coverage_score": 74.8,
"coverage_score": 78.5,
"exclude_path": "",
"crate_features": ""
}
125 changes: 125 additions & 0 deletions src/configurator/aarch64/fdt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// 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 loading the device tree.

use vm_memory::{Bytes, GuestMemory};

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

use crate::configurator::{BootConfigurator, BootParams, Error as BootConfiguratorError, Result};

/// Errors specific to the device tree boot protocol configuration.
#[derive(Debug, PartialEq)]
pub enum Error {
/// FDT does not fit in guest memory.
FDTPastRamEnd,
/// Error writing FDT in memory.
WriteFDTToMemory,
}

impl StdError for Error {
fn description(&self) -> &str {
use Error::*;
match self {
FDTPastRamEnd => "FDT does not fit in guest memory.",
WriteFDTToMemory => "Error writing FDT in guest memory.",
}
}
}

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

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

/// Boot configurator for device tree.
pub struct FdtBootConfigurator {}

impl BootConfigurator for FdtBootConfigurator {
/// Writes the boot parameters (configured elsewhere) into guest memory.
///
/// # Arguments
///
/// * `params` - boot parameters containing the FDT.
/// * `guest_memory` - guest's physical memory.
fn write_bootparams<M>(params: BootParams, guest_memory: &M) -> Result<()>
where
M: GuestMemory,
{
guest_memory
.checked_offset(params.header_start, params.header.len())
.ok_or(Error::FDTPastRamEnd)?;

// The VMM has filled an FDT and passed it as a `ByteValued` object.
guest_memory
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to validate guest memory address here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added validation. write_slice would have returned an error, but the other use cases explicitly validate that the bootparams fit in guest memory, so I added the same here.

.write_slice(params.header.as_slice(), params.header_start)
.map_err(|_| Error::WriteFDTToMemory.into())
}
}

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

const FDT_MAX_SIZE: usize = 0x20;
const MEM_SIZE: u64 = 0x100_0000;

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

#[derive(Clone, Copy, Default)]
struct FdtPlaceholder([u8; FDT_MAX_SIZE]);
unsafe impl ByteValued for FdtPlaceholder {}

#[test]
fn test_configure_fdt_boot() {
let fdt = FdtPlaceholder([0u8; FDT_MAX_SIZE]);
let guest_memory = create_guest_mem();

// Error case: FDT doesn't fit in guest memory.
let fdt_addr = GuestAddress(guest_memory.last_addr().raw_value() - FDT_MAX_SIZE as u64 + 1);
assert_eq!(
FdtBootConfigurator::write_bootparams::<GuestMemoryMmap>(
BootParams::new::<FdtPlaceholder>(&fdt, fdt_addr),
&guest_memory,
)
.err(),
Some(Error::FDTPastRamEnd.into())
);

let fdt_addr = GuestAddress(guest_memory.last_addr().raw_value() - FDT_MAX_SIZE as u64);
assert!(FdtBootConfigurator::write_bootparams::<GuestMemoryMmap>(
BootParams::new::<FdtPlaceholder>(&fdt, fdt_addr),
&guest_memory,
)
.is_ok());
}

#[test]
fn test_error_messages() {
assert_eq!(
format!("{}", Error::FDTPastRamEnd),
"Device Tree Boot Configurator Error: FDT does not fit in guest memory."
);
assert_eq!(
format!("{}", Error::WriteFDTToMemory),
"Device Tree Boot Configurator Error: Error writing FDT in guest memory."
);
}
}
9 changes: 9 additions & 0 deletions src/configurator/aarch64/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// 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")]

pub mod fdt;
Loading