cloud-hypervisor/vmm/src/vm.rs

272 lines
8.0 KiB
Rust
Raw Normal View History

// Copyright © 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
extern crate arch;
extern crate kvm_ioctls;
extern crate linux_loader;
extern crate vm_memory;
use kvm_bindings::kvm_userspace_memory_region;
use kvm_ioctls::*;
use linux_loader::cmdline;
use linux_loader::loader::KernelLoader;
use std::ffi::CString;
use std::fs::File;
use std::path::Path;
use std::{io, result, str, thread};
use vm_memory::{
Address, Bytes, GuestAddress, GuestMemory, GuestMemoryMmap, GuestMemoryRegion, GuestUsize,
MmapError,
};
const DEFAULT_CMDLINE: &str =
"console=ttyS0,115200n8 init=/init tsc=reliable no_timer_check cryptomgr.notests";
const CMDLINE_OFFSET: GuestAddress = GuestAddress(0x20000);
/// Errors associated with the wrappers over KVM ioctls.
#[derive(Debug)]
pub enum Error {
/// Cannot open the VM file descriptor.
VmFd(io::Error),
/// Cannot create the KVM instance
VmCreate(io::Error),
/// Cannot set the VM up
VmSetup(io::Error),
/// Cannot open the kernel image
KernelFile(io::Error),
/// Mmap backed guest memory error
GuestMemory(MmapError),
/// Cannot load the kernel in memory
KernelLoad(linux_loader::loader::Error),
/// Cannot load the command line in memory
CmdLine,
}
pub type Result<T> = result::Result<T, Error>;
struct VmConfig<'a> {
kernel_path: &'a Path,
cmdline: Option<cmdline::Cmdline>,
cmdline_addr: GuestAddress,
memory_size: GuestUsize,
vcpu_count: u8,
}
impl<'a> Default for VmConfig<'a> {
fn default() -> Self {
let line = String::from(DEFAULT_CMDLINE);
let mut cmdline = cmdline::Cmdline::new(line.capacity());
cmdline.insert_str(line);
VmConfig {
kernel_path: Path::new(""),
cmdline: Some(cmdline),
cmdline_addr: CMDLINE_OFFSET,
memory_size: 512,
vcpu_count: 1,
}
}
}
pub struct Vm<'a> {
fd: VmFd,
kernel: File,
memory: GuestMemoryMmap,
vcpus: Option<Vec<thread::JoinHandle<()>>>,
config: VmConfig<'a>,
}
impl<'a> Vm<'a> {
pub fn new(kvm: &Kvm, kernel_path: &'a Path) -> Result<Self> {
let vm_config = VmConfig {
kernel_path,
..Default::default()
};
let kernel = File::open(kernel_path).map_err(Error::KernelFile)?;
let fd = kvm.create_vm().map_err(Error::VmCreate)?;
// Init guest memory
let arch_mem_regions = arch::arch_memory_regions(vm_config.memory_size << 20);
let guest_memory = GuestMemoryMmap::new(&arch_mem_regions).map_err(Error::GuestMemory)?;
guest_memory
.with_regions(|index, region| {
let mem_region = kvm_userspace_memory_region {
slot: index as u32,
guest_phys_addr: region.start_addr().raw_value(),
memory_size: region.len() as u64,
userspace_addr: region.as_ptr() as u64,
flags: 0,
};
println!(
"Size {:?} guest addr 0x{:x} host addr 0x{:x}",
mem_region.memory_size, mem_region.guest_phys_addr, mem_region.userspace_addr
);
// Safe because the guest regions are guaranteed not to overlap.
fd.set_user_memory_region(mem_region)
})
.map_err(|_| Error::GuestMemory(MmapError::NoMemoryRegion))?;
// Set TSS
fd.set_tss_address(arch::x86_64::layout::KVM_TSS_ADDRESS.raw_value() as usize)
.map_err(Error::VmSetup)?;
Ok(Vm {
fd,
kernel,
memory: guest_memory,
vcpus: None,
config: vm_config,
})
}
pub fn load_kernel(&mut self) -> Result<GuestAddress> {
let cmdline = self.config.cmdline.clone().ok_or(Error::CmdLine)?;
let cmdline_cstring = CString::new(cmdline).map_err(|_| Error::CmdLine)?;
let entry_addr = linux_loader::loader::Elf::load(
&self.memory,
None,
&mut self.kernel,
Some(arch::HIMEM_START),
)
.map_err(Error::KernelLoad)?;
linux_loader::loader::load_cmdline(
&self.memory,
self.config.cmdline_addr,
&cmdline_cstring,
)
.map_err(|_| Error::CmdLine)?;
let vcpu_count = self.config.vcpu_count;
arch::configure_system(
&self.memory,
self.config.cmdline_addr,
cmdline_cstring.to_bytes().len() + 1,
vcpu_count,
)
.map_err(|_| Error::CmdLine)?;
Ok(entry_addr.kernel_load)
}
pub fn start(&mut self) -> Result<()> {
Ok(())
}
}
#[allow(unused)]
pub fn test_vm() {
// This example based on https://lwn.net/Articles/658511/
let code = [
0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
0x00, 0xd8, /* add %bl, %al */
0x04, b'0', /* add $'0', %al */
0xee, /* out %al, (%dx) */
0xb0, b'\n', /* mov $'\n', %al */
0xee, /* out %al, (%dx) */
0xf4, /* hlt */
];
let mem_size = 0x1000;
let load_addr = GuestAddress(0x1000);
let mem = GuestMemoryMmap::new(&[(load_addr, mem_size)]).unwrap();
let kvm = Kvm::new().expect("new KVM instance creation failed");
let vm_fd = kvm.create_vm().expect("new VM fd creation failed");
mem.with_regions(|index, region| {
let mem_region = kvm_userspace_memory_region {
slot: index as u32,
guest_phys_addr: region.start_addr().raw_value(),
memory_size: region.len() as u64,
userspace_addr: region.as_ptr() as u64,
flags: 0,
};
// Safe because the guest regions are guaranteed not to overlap.
vm_fd.set_user_memory_region(mem_region)
})
.expect("Cannot configure guest memory");
mem.write_slice(&code, load_addr)
.expect("Writing code to memory failed");
let vcpu_fd = vm_fd.create_vcpu(0).expect("new VcpuFd failed");
let mut vcpu_sregs = vcpu_fd.get_sregs().expect("get sregs failed");
vcpu_sregs.cs.base = 0;
vcpu_sregs.cs.selector = 0;
vcpu_fd.set_sregs(&vcpu_sregs).expect("set sregs failed");
let mut vcpu_regs = vcpu_fd.get_regs().expect("get regs failed");
vcpu_regs.rip = 0x1000;
vcpu_regs.rax = 2;
vcpu_regs.rbx = 3;
vcpu_regs.rflags = 2;
vcpu_fd.set_regs(&vcpu_regs).expect("set regs failed");
loop {
match vcpu_fd.run().expect("run failed") {
VcpuExit::IoIn(addr, data) => {
println!(
"IO in -- addr: {:#x} data [{:?}]",
addr,
str::from_utf8(&data).unwrap()
);
}
VcpuExit::IoOut(addr, data) => {
println!(
"IO out -- addr: {:#x} data [{:?}]",
addr,
str::from_utf8(&data).unwrap()
);
}
VcpuExit::MmioRead(_addr, _data) => {}
VcpuExit::MmioWrite(_addr, _data) => {}
VcpuExit::Unknown => {}
VcpuExit::Exception => {}
VcpuExit::Hypercall => {}
VcpuExit::Debug => {}
VcpuExit::Hlt => {
println!("HLT");
}
VcpuExit::IrqWindowOpen => {}
VcpuExit::Shutdown => {}
VcpuExit::FailEntry => {}
VcpuExit::Intr => {}
VcpuExit::SetTpr => {}
VcpuExit::TprAccess => {}
VcpuExit::S390Sieic => {}
VcpuExit::S390Reset => {}
VcpuExit::Dcr => {}
VcpuExit::Nmi => {}
VcpuExit::InternalError => {}
VcpuExit::Osi => {}
VcpuExit::PaprHcall => {}
VcpuExit::S390Ucontrol => {}
VcpuExit::Watchdog => {}
VcpuExit::S390Tsch => {}
VcpuExit::Epr => {}
VcpuExit::SystemEvent => {}
VcpuExit::S390Stsi => {}
VcpuExit::IoapicEoi => {}
VcpuExit::Hyperv => {}
}
// r => panic!("unexpected exit reason: {:?}", r),
}
}