Skip to content

Usage examples, doctests & cosmetic tweaks #23

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

Merged
merged 1 commit into from
May 26, 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": 80.3,
"coverage_score": 80.4,
"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": 78.5,
"coverage_score": 78.7,
"exclude_path": "",
"crate_features": ""
}
103 changes: 82 additions & 21 deletions src/cmdline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use std::fmt;
use std::result;

/// The error type for command line building operations.
#[derive(PartialEq, Debug)]
#[derive(Debug, PartialEq)]
pub enum Error {
/// Operation would have resulted in a non-printable ASCII character.
InvalidAscii,
Expand All @@ -30,16 +30,17 @@ impl fmt::Display for Error {
f,
"{}",
match *self {
Error::InvalidAscii => "string contains non-printable ASCII character",
Error::HasSpace => "string contains a space",
Error::HasEquals => "string contains an equals sign",
Error::TooLarge => "inserting string would make command line too long",
Error::InvalidAscii => "String contains a non-printable ASCII character.",
Error::HasSpace => "String contains a space.",
Error::HasEquals => "String contains an equals sign.",
Error::TooLarge => "Inserting string would make command line too long.",
}
)
}
}

/// Specialized Result type for command line operations.
/// Specialized [`Result`] type for command line operations.
/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
pub type Result<T> = result::Result<T, Error>;

fn valid_char(c: char) -> bool {
Expand Down Expand Up @@ -69,17 +70,37 @@ fn valid_element(s: &str) -> Result<()> {
}
}

/// A builder for a kernel command line string that validates the string as its being built. A
/// `CString` can be constructed from this directly using `CString::new`.
#[derive(Clone)]
/// A builder for a kernel command line string that validates the string as it's being built.
/// A `CString` can be constructed from this directly using `CString::new`.
///
/// # Examples
///
/// ```rust
/// # use linux_loader::cmdline::*;
/// # use std::ffi::CString;
/// let cl = Cmdline::new(100);
/// let cl_cstring = CString::new(cl).unwrap();
/// assert_eq!(cl_cstring.to_str().unwrap(), "");
/// ```
Copy link
Member

Choose a reason for hiding this comment

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

#[derive(Clone)] is gone.

Copy link
Member Author

Choose a reason for hiding this comment

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

It doesn't appear to be needed anywhere, anyway.

pub struct Cmdline {
line: String,
capacity: usize,
}

impl Cmdline {
/// Constructs an empty Cmdline with the given capacity, which includes the nul terminator.
/// Capacity must be greater than 0.
/// Constructs an empty [`Cmdline`] with the given capacity, including the nul terminator.
///
/// # Arguments
///
/// * `capacity` - Command line capacity. Must be greater than 0.
///
/// # Examples
///
/// ```rust
/// # use linux_loader::cmdline::*;
/// let cl = Cmdline::new(100);
/// ```
/// [`Cmdline`]: struct.Cmdline.html
pub fn new(capacity: usize) -> Cmdline {
assert_ne!(capacity, 0);
Cmdline {
Expand Down Expand Up @@ -109,7 +130,23 @@ impl Cmdline {
assert!(self.line.len() < self.capacity);
}

/// Validates and inserts a key value pair into this command line
/// Validates and inserts a key-value pair into this command line.
///
/// # Arguments
///
/// * `key` - Key to be inserted in the command line string.
/// * `val` - Value corresponding to `key`.
///
/// # Examples
///
/// ```rust
/// # use linux_loader::cmdline::*;
/// # use std::ffi::CString;
/// let mut cl = Cmdline::new(100);
/// cl.insert("foo", "bar");
/// let cl_cstring = CString::new(cl).unwrap();
/// assert_eq!(cl_cstring.to_str().unwrap(), "foo=bar");
/// ```
pub fn insert<T: AsRef<str>>(&mut self, key: T, val: T) -> Result<()> {
let k = key.as_ref();
let v = val.as_ref();
Expand All @@ -127,7 +164,22 @@ impl Cmdline {
Ok(())
}

/// Validates and inserts a string to the end of the current command line
/// Validates and inserts a string to the end of the current command line.
///
/// # Arguments
///
/// * `slug` - String to be appended to the command line.
///
/// # Examples
///
/// ```rust
/// # use linux_loader::cmdline::*;
/// # use std::ffi::CString;
/// let mut cl = Cmdline::new(100);
/// cl.insert_str("foobar");
/// let cl_cstring = CString::new(cl).unwrap();
/// assert_eq!(cl_cstring.to_str().unwrap(), "foobar");
/// ```
pub fn insert_str<T: AsRef<str>>(&mut self, slug: T) -> Result<()> {
let s = slug.as_ref();
valid_str(s)?;
Expand All @@ -141,7 +193,16 @@ impl Cmdline {
Ok(())
}

/// Returns the cmdline in progress without nul termination
/// Returns the string representation of the command line without the nul terminator.
///
/// # Examples
///
/// ```rust
/// # use linux_loader::cmdline::*;
/// let mut cl = Cmdline::new(10);
/// cl.insert_str("foobar");
/// assert_eq!(cl.as_str(), "foobar");
/// ```
pub fn as_str(&self) -> &str {
self.line.as_str()
}
Expand All @@ -159,7 +220,7 @@ mod tests {
use std::ffi::CString;

#[test]
fn insert_hello_world() {
fn test_insert_hello_world() {
let mut cl = Cmdline::new(100);
assert_eq!(cl.as_str(), "");
assert!(cl.insert("hello", "world").is_ok());
Expand All @@ -170,15 +231,15 @@ mod tests {
}

#[test]
fn insert_multi() {
fn test_insert_multi() {
let mut cl = Cmdline::new(100);
assert!(cl.insert("hello", "world").is_ok());
assert!(cl.insert("foo", "bar").is_ok());
assert_eq!(cl.as_str(), "hello=world foo=bar");
}

#[test]
fn insert_space() {
fn test_insert_space() {
let mut cl = Cmdline::new(100);
assert_eq!(cl.insert("a ", "b"), Err(Error::HasSpace));
assert_eq!(cl.insert("a", "b "), Err(Error::HasSpace));
Expand All @@ -188,7 +249,7 @@ mod tests {
}

#[test]
fn insert_equals() {
fn test_insert_equals() {
let mut cl = Cmdline::new(100);
assert_eq!(cl.insert("a=", "b"), Err(Error::HasEquals));
assert_eq!(cl.insert("a", "b="), Err(Error::HasEquals));
Expand All @@ -199,15 +260,15 @@ mod tests {
}

#[test]
fn insert_emoji() {
fn test_insert_emoji() {
let mut cl = Cmdline::new(100);
assert_eq!(cl.insert("heart", "💖"), Err(Error::InvalidAscii));
assert_eq!(cl.insert("💖", "love"), Err(Error::InvalidAscii));
assert_eq!(cl.as_str(), "");
}

#[test]
fn insert_string() {
fn test_insert_string() {
let mut cl = Cmdline::new(13);
assert_eq!(cl.as_str(), "");
assert!(cl.insert_str("noapic").is_ok());
Expand All @@ -217,7 +278,7 @@ mod tests {
}

#[test]
fn insert_too_large() {
fn test_insert_too_large() {
let mut cl = Cmdline::new(4);
assert_eq!(cl.insert("hello", "world"), Err(Error::TooLarge));
assert_eq!(cl.insert("a", "world"), Err(Error::TooLarge));
Expand Down
9 changes: 6 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@
//!
//! This crate offers support for loading raw ELF (vmlinux), compressed
//! big zImage (bzImage) and PE (Image) kernel images.
//! ELF support includes the Linux and PVH boot protocols.
//! Support for any other kernel image format can be added by implementing
//! the KernelLoader.
//! the [`KernelLoader`] and [`BootConfigurator`].
//!
//! # Platform support
//!
//! - x86_64
//! - ARM64
//! - `x86_64`
//! - `ARM64`
//!
//! [`BootConfigurator`]: trait.BootConfigurator.html
//! [`KernelLoader`]: trait.KernelLoader.html

pub mod cmdline;
pub mod configurator;
Expand Down
61 changes: 44 additions & 17 deletions src/loader/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@
//
// SPDX-License-Identifier: Apache-2.0 AND BSD-3-Clause

//! Traits and Structs for loading kernels into guest memory.
//! - [KernelLoader](trait.KernelLoader.html): load kernel image into guest memory
//! - [KernelLoaderResult](struct.KernelLoaderResult.html): the structure which loader
//! returns to VMM to assist zero page construction and boot environment setup
//! - [Elf](struct.Elf.html): elf image loader
//! - [BzImage](struct.BzImage.html): bzImage loader
//! Traits and structs for loading kernels into guest memory.
//! - [KernelLoader](trait.KernelLoader.html): load kernel image into guest memory.
//! - [KernelLoaderResult](struct.KernelLoaderResult.html): structure passed to the VMM to assist
//! zero page construction and boot environment setup.
//! - [Elf](struct.Elf.html): elf image loader.
//! - [BzImage](struct.BzImage.html): bzImage loader.

extern crate vm_memory;

Expand Down Expand Up @@ -71,7 +71,8 @@ pub enum Error {
MemoryOverflow,
}

/// A specialized `Result` type for the kernel loader.
/// A specialized [`Result`] type for the kernel loader.
/// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
pub type Result<T> = std::result::Result<T, Error>;

impl StdError for Error {
Expand Down Expand Up @@ -126,13 +127,13 @@ impl From<pe::Error> for Error {
/// the VMM.
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct KernelLoaderResult {
/// Address in the guest memory where the kernel image starts to be loaded
/// Address in the guest memory where the kernel image starts to be loaded.
pub kernel_load: GuestAddress,
/// Offset in guest memory corresponding to the end of kernel image, in case that
/// device tree blob and initrd will be loaded adjacent to kernel image.
/// Offset in guest memory corresponding to the end of kernel image, in case the device tree
/// blob and initrd will be loaded adjacent to kernel image.
pub kernel_end: GuestUsize,
/// This field is only for bzImage following https://www.kernel.org/doc/Documentation/x86/boot.txt
/// VMM should make use of it to fill zero page for bzImage direct boot.
/// Configuration for the VMM to use to fill zero page for bzImage direct boot.
/// See https://www.kernel.org/doc/Documentation/x86/boot.txt.
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
pub setup_header: Option<bootparam::setup_header>,
/// This field optionally holds the address of a PVH entry point, indicating that
Expand All @@ -141,10 +142,18 @@ pub struct KernelLoaderResult {
pub pvh_entry_addr: Option<GuestAddress>,
}

/// A kernel image loading support must implement the KernelLoader trait.
/// The only method to be implemented is the load one, returning a KernelLoaderResult structure.
/// Trait that specifies kernel image loading support.
pub trait KernelLoader {
/// How to load a specific kernel image format into the guest memory.
///
/// # Arguments
///
/// * `guest_mem`: [`GuestMemory`] to load the kernel in.
/// * `kernel_start`: Address in guest memory where the kernel is loaded.
/// * `kernel_image`: Kernel image to be loaded.
/// * `highmem_start_address`: Address where high memory starts.
///
/// [`GuestMemory`]: https://docs.rs/vm-memory/latest/vm_memory/guest_memory/trait.GuestMemory.html
fn load<F, M: GuestMemory>(
guest_mem: &M,
kernel_start: Option<GuestAddress>,
Expand All @@ -164,9 +173,27 @@ unsafe impl ByteValued for bootparam::boot_params {}
///
/// # Arguments
///
/// * `guest_mem` - A u8 slice that will be partially overwritten by the command line.
/// * `guest_mem` - [`GuestMemory`] that will be partially overwritten by the command line.
/// * `guest_addr` - The address in `guest_mem` at which to load the command line.
/// * `cmdline` - The kernel command line.
///
/// [`GuestMemory`]: https://docs.rs/vm-memory/latest/vm_memory/guest_memory/trait.GuestMemory.html
///
/// # Examples
///
/// ```rust
/// # extern crate vm_memory;
/// # use linux_loader::loader::*;
/// # use vm_memory::{Bytes, GuestAddress, GuestMemoryMmap};
/// # use std::ffi::CStr;
/// let mem_size: usize = 0x1000000;
/// let gm = GuestMemoryMmap::from_ranges(&[(GuestAddress(0x0), mem_size)]).unwrap();
/// let cl = CStr::from_bytes_with_nul(b"foo=bar\0").unwrap();
/// let mut buf = vec![0u8;8];
/// let result = load_cmdline(&gm, GuestAddress(0x1000), &cl).unwrap();
/// gm.read_slice(buf.as_mut_slice(), GuestAddress(0x1000)).unwrap();
/// assert_eq!(buf.as_slice(), "foo=bar\0".as_bytes());
///
pub fn load_cmdline<M: GuestMemory>(
guest_mem: &M,
guest_addr: GuestAddress,
Expand Down Expand Up @@ -203,7 +230,7 @@ mod tests {
}

#[test]
fn cmdline_overflow() {
fn test_cmdline_overflow() {
let gm = create_guest_mem();
let cmdline_address = GuestAddress(MEM_SIZE - 5);
assert_eq!(
Expand All @@ -217,7 +244,7 @@ mod tests {
}

#[test]
fn cmdline_write_end() {
fn test_cmdline_write_end() {
let gm = create_guest_mem();
let mut cmdline_address = GuestAddress(45);
assert_eq!(
Expand Down